Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
a61d097
ci: bump pinned reusable workflow to fe4b1f0
tastybento Jul 1, 2026
3075c21
Merge pull request #404 from BentoBoxWorld/ci/bump-publish-workflow-f…
tastybento Jul 1, 2026
0625dd3
ci: bump pinned publish-platforms.yml to ca2dcd1
tastybento Jul 3, 2026
4ec69b9
Merge pull request #405 from BentoBoxWorld/ci/bump-publish-platforms-…
tastybento Jul 3, 2026
e2f8a7b
Honour per-challenge removeWhenCompleted flag in player GUI
tastybento Jul 10, 2026
ef08966
Add setting to open the challenges GUI while off-island
tastybento Jul 10, 2026
3532b66
Run level-completion check when challenges are completed by admins
tastybento Jul 10, 2026
658acd5
Merge pull request #406 from BentoBoxWorld/fix/337-remove-when-completed
tastybento Jul 10, 2026
bc8709d
Merge pull request #407 from BentoBoxWorld/feat/349-open-gui-anywhere
tastybento Jul 10, 2026
599874f
Merge pull request #408 from BentoBoxWorld/fix/385-admin-complete-lev…
tastybento Jul 10, 2026
0701389
Compare base potion type when metadata is ignored
tastybento Jul 10, 2026
f888514
Add tests verifying item/block/entity/money/XP consumption on completion
tastybento Jul 10, 2026
a5983de
Add challenges_completed_percent placeholder
tastybento Jul 10, 2026
e40c33e
Add island level rewards via the Level addon
tastybento Jul 10, 2026
15eb3ae
Add optional per-challenge reward chance
tastybento Jul 10, 2026
003a872
Merge pull request #410 from BentoBoxWorld/feat/352-percent-placeholder
tastybento Jul 10, 2026
c60619e
Merge pull request #413 from BentoBoxWorld/test/111-removal-coverage
tastybento Jul 10, 2026
a3a8f32
Address SonarCloud findings on PR #409
tastybento Jul 10, 2026
da91fcc
Reduce duplication flagged by SonarCloud
tastybento Jul 10, 2026
c38d6c9
Add reward chance translations for all locales
tastybento Jul 10, 2026
fb60c97
Merge origin/develop into fix/320-potion-ignore-metadata
tastybento Jul 10, 2026
5760388
Merge pull request #409 from BentoBoxWorld/fix/320-potion-ignore-meta…
tastybento Jul 10, 2026
8fc4946
Merge origin/develop into feat/279-island-level-reward
tastybento Jul 10, 2026
eb8d91a
Merge pull request #411 from BentoBoxWorld/feat/279-island-level-reward
tastybento Jul 10, 2026
39f3c76
Merge origin/develop into feat/76-reward-chance
tastybento Jul 10, 2026
5a68995
Merge pull request #412 from BentoBoxWorld/feat/76-reward-chance
tastybento Jul 10, 2026
dcc65ff
Fix #323: hide level unlock message on locked levels
tastybento Jul 10, 2026
b56b8cf
Improve #329: tell players how to answer confirmation prompts
tastybento Jul 10, 2026
add3daa
Add include-undeployed to shipped config.yml (#179)
tastybento Jul 10, 2026
ab7ea60
Merge pull request #414 from BentoBoxWorld/fix/323-locked-level-congrats
tastybento Jul 11, 2026
c0215c9
Merge pull request #415 from BentoBoxWorld/feat/329-confirm-instruction
tastybento Jul 11, 2026
334216f
Merge pull request #416 from BentoBoxWorld/feat/179-include-undeploye…
tastybento Jul 11, 2026
65ebe61
Implement TOGGLEABLE undeployed view mode (#175)
tastybento Jul 11, 2026
003b90b
Add default colour for challenge description and reward text (#340)
tastybento Jul 11, 2026
f699c90
Merge pull request #417 from BentoBoxWorld/feat/175-toggleable-undepl…
tastybento Jul 11, 2026
bb27ccc
Merge pull request #418 from BentoBoxWorld/feat/340-default-text-color
tastybento Jul 11, 2026
76a35d1
Add biome requirement to island challenges (#335)
tastybento Jul 11, 2026
2f74680
Address SonarCloud findings on #419
tastybento Jul 11, 2026
fd8cab4
Merge pull request #419 from BentoBoxWorld/feat/335-biome-requirement
tastybento Jul 11, 2026
84db795
Prepare 1.8.0 release: bump version, refresh CLAUDE.md deps
tastybento Jul 11, 2026
4fbd7be
Add plugwright E2E in-game test spike
tastybento Jul 11, 2026
b58147b
Make e2e workflow a proper on-demand regression harness
tastybento Jul 11, 2026
704531e
Fix GitHub Actions security findings in e2e workflow
tastybento Jul 11, 2026
a2248a1
Merge pull request #422 from BentoBoxWorld/spike/plugwright-e2e
tastybento Jul 11, 2026
b2740f2
Pin E2E npm deps via committed lockfile (fixes CI 1.21.11 support)
tastybento Jul 11, 2026
152ef0c
Merge pull request #423 from BentoBoxWorld/fix/e2e-pin-mineflayer
tastybento Jul 11, 2026
4ffa54e
Add E2E toggle tests for 1.8.0 settings (#349, #179)
tastybento Jul 11, 2026
71b8f0e
Merge pull request #424 from BentoBoxWorld/fix/e2e-pin-mineflayer
tastybento Jul 12, 2026
4399087
Address Copilot review on the 1.8.0 release PR (#421)
tastybento Jul 12, 2026
1826b59
Merge pull request #425 from BentoBoxWorld/fix/pr421-copilot-review
tastybento Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: E2E (plugwright)

# In-game end-to-end tests via plugwright (boots a real Paper server + Mineflayer bot) —
# the layer the Maven/MockBukkit unit tests can't reach. Run ON DEMAND as a regression check.
#
# Trigger it from the GitHub UI (Actions tab -> "E2E (plugwright)" -> "Run workflow", pick the
# branch/tag) or from the CLI: gh workflow run e2e.yml --ref <branch-or-tag>
#
# It is deliberately NOT wired to every push/PR: a run boots a server and generates worlds
# (~1-2 min) and is heavier/flakier than the unit tests. For a nightly regression, add:
# schedule:
# - cron: "0 3 * * *"
on:
workflow_dispatch:
inputs:
test_names:
description: "Only run tests whose name contains this (comma-separated substrings). Blank = all."
required: false
default: ""

jobs:
e2e:
name: E2E tests
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Set up Gradle (with caching)
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4

# Reuse the Paper jar + downloaded libraries and the BentoBox/BSkyBlock jars across runs.
# plugwright's clean step preserves server.jar / cache / libraries, and build.gradle.kts
# caches the dependency jars in .deps, so this avoids re-downloading ~60 MB every run.
- name: Cache Paper + dependency jars
uses: actions/cache@v4
with:
path: |
e2e/.deps
e2e/run/server.jar
e2e/run/cache
e2e/run/libraries
key: plugwright-${{ hashFiles('e2e/build.gradle.kts') }}

- name: Cache npm modules
uses: actions/cache@v4
with:
path: e2e/src/test/e2e/node_modules
# Key on the committed lockfile so the cache tracks the exact resolved versions
# (mineflayer/minecraft-data must support the target Minecraft version).
key: e2e-npm-${{ hashFiles('e2e/src/test/e2e/package-lock.json') }}

- name: Build Challenges jar (Maven)
run: mvn -B -DskipTests package

- name: Run E2E tests
working-directory: e2e
env:
PLUGWRIGHT_DEBUG: "1"
# Bind the user-controlled input to an env var and reference it as a shell variable
# below — never interpolate ${{ inputs.* }} directly into a run block (script injection).
TEST_NAMES: ${{ inputs.test_names }}
run: |
if [ -n "$TEST_NAMES" ]; then
./gradlew plugwrightTest "-PtestNames=$TEST_NAMES" --no-daemon
else
./gradlew plugwrightTest --no-daemon
fi

# Always upload the server log + any plugwright report so a regression is debuggable.
- name: Upload server log & report
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-logs
path: |
e2e/run/logs/**
e2e/run/plugwright-report/**
if-no-files-found: ignore
retention-days: 14
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ on:

jobs:
publish:
uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@71bf927bce32586216baa6995f21852d944b98b9 # master
uses: bentoboxworld/.github/.github/workflows/publish-platforms.yml@ca2dcd167e8db4e0f671a976080744dda43801a6 # master
with:
use_release_asset: "true" # publish the jar attached to the release; do not rebuild
hangar_slug: "Challenges-For-BentoBox" # blank = skip Hangar
Expand All @@ -27,4 +27,4 @@ jobs:
version: ${{ inputs.version }} # empty on release events -> falls back to the release tag
secrets:
HANGAR_API_KEY: ${{ secrets.HANGAR_API_KEY }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

Challenges is a BentoBox addon (Bukkit/Spigot plugin module) that adds player challenges to any BentoBox GameMode addon (BSkyBlock, AcidIsland, SkyGrid, CaveBlock). It is not a standalone plugin — it is loaded by BentoBox at runtime.

- Java 21, Maven, Spigot `1.21.3-R0.1-SNAPSHOT`
- Depends on BentoBox `3.4.0`, optionally Level `2.6.3` and Vault `1.7`
- Java 21, Maven, Paper `1.21.11-R0.1-SNAPSHOT` (server API dep is `paper.version` in `pom.xml`; `addon.yml` declares `api-version: 3.12.0`)
- Depends on BentoBox `3.14.0`, optionally Level `2.6.3` and Vault `1.7`
- Version is set via `<build.version>` in `pom.xml` (uses `${revision}` / CI `-bNNN` suffix)

## Commands
Expand Down
12 changes: 12 additions & 0 deletions e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Plugwright server run directory (Paper jar, worlds, downloaded plugins, logs)
run/
# Cached dependency jars (BentoBox, BSkyBlock) downloaded by build.gradle.kts
.deps/
# Gradle
.gradle/
build/
# Node / TypeScript test artifacts
src/test/e2e/node_modules/
src/test/e2e/dist/
# package-lock.json IS committed — it pins mineflayer/minecraft-data to versions that
# support the target Minecraft version, so CI installs are reproducible.
57 changes: 57 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# End-to-end (in-game) tests — plugwright spike

This is a spike using [plugwright](https://github.com/Drownek/plugwright) to verify Challenges
**in a real running server** — something the JUnit/MockBukkit unit tests can't do. Plugwright boots
a real Paper server, loads the plugins, and drives a headless [Mineflayer](https://github.com/PrismarineJS/mineflayer)
bot that runs commands and clicks GUIs, then asserts on what the bot sees.

It is a **separate Gradle sidecar** — it does not touch the Maven build. It deploys *prebuilt* jars:

- **BentoBox 3.14.0** and **BSkyBlock 1.20.0** are downloaded (cached in `.deps/`).
- The **Challenges** jar is taken from `../target/` (build it first with `mvn -DskipTests package`).

> **Gotcha that cost most of the spike:** BentoBox *addons* (BSkyBlock, Challenges) must be staged
> into `plugins/BentoBox/addons/`, **not** `plugins/`. In `plugins/` they load as bare Paper plugins
> and never register as gamemodes, so no `bskyblock_world` is created and every `/island`,
> `/bsbadmin`, `/challenges` command comes back "Unknown". See `build.gradle.kts`.

## Running locally

```bash
# 1. Build the Challenges jar (Maven)
mvn -DskipTests package

# 2. Run the E2E tests (needs Java 21, Node 18+)
cd e2e
JAVA_HOME=/path/to/jdk-21 ./gradlew plugwrightTest

# add PLUGWRIGHT_DEBUG=1 to see every message the bot receives
```

First run downloads Paper 1.21.11 (~50 MB) and the plugin jars; later runs reuse them
(`run/server.jar`, `.deps/`). A full run is ~30–40s (server boot + world gen dominate).

## What the tests cover (`src/test/e2e/challenges.spec.ts`)

- **`bot can interact with the server`** — smoke test: bot joins Paper 1.21.11, ops, runs a command,
receives the reply. Proves the whole stack (BentoBox 3.14.0 + BSkyBlock + Challenges 1.8.0) loads
and is drivable.
- **`confirmation prompts tell the player how to answer (#329)`** — opens the Challenges admin GUI,
clicks "Challenge Wipe", and asserts the bot receives the confirmation instruction line
("Type 'confirm' ... or 'cancel' ...") added in #415. A real in-game validation of a 1.8.0 feature.
- **`open-anywhere setting toggles in the admin settings GUI (#349)`** — opens admin → Settings,
finds the new "Open GUI Anywhere" (Elytra) toggle, clicks it and asserts the Enabled/Disabled
lore flips, then clicks again to restore it. Proves the full config → Settings → panel button →
click-handler → saveSettings chain works on a live server.
- **`include-undeployed setting toggles in the admin settings GUI (#179)`** — same live toggle check
for the "Include Undeployed Challenges" (Barrel) button that now ships in config.yml.

Both toggle tests are self-restoring (they read the current state, flip it, assert, then flip back),
so they don't depend on the persisted run-dir config or the order the tests run in.

## Notes / next steps

- Tests requiring an **island** (block/biome/completion flows) still need island bootstrap — either
a bot-driven `/island create` (blueprint GUI) or a pre-staged world via `writeFiles`.
- In CI this runs via `.github/workflows/e2e.yml` (manual `workflow_dispatch` for now — advisory,
not a required check).
57 changes: 57 additions & 0 deletions e2e/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import java.net.URI

plugins {
// Plugwright: boots a real Paper server, deploys plugins, drives Mineflayer bots.
id("io.github.drownek.plugwright") version "2.0.2"
}

// --- Dependency jars -------------------------------------------------------------------------
// BentoBox is a Paper plugin (goes in plugins/). BSkyBlock and Challenges are BentoBox *addons*
// and MUST live in plugins/BentoBox/addons/ so BentoBox loads them as gamemode/addon (creating
// the gamemode world and registering commands) — dropping them straight into plugins/ makes them
// load as bare Paper plugins that never register with BentoBox.

val depsDir = layout.projectDirectory.dir(".deps").asFile.apply { mkdirs() }

fun cachedJar(name: String, url: String): File {
val f = File(depsDir, name)
if (!f.exists()) {
logger.lifecycle("plugwright: downloading $name ...")
URI(url).toURL().openStream().use { input -> f.outputStream().use { input.copyTo(it) } }
}
return f
}

val bentoboxJar = cachedJar(
"BentoBox-3.14.0.jar",
"https://github.com/BentoBoxWorld/BentoBox/releases/download/3.14.0/BentoBox-3.14.0.jar"
)
val bskyblockJar = cachedJar(
"BSkyBlock-1.20.0.jar",
"https://github.com/BentoBoxWorld/BSkyBlock/releases/download/1.20.0/BSkyBlock-1.20.0.jar"
)

// Maven-built Challenges jar (version/profile suffix varies: -SNAPSHOT-LOCAL locally, plain on CI).
val challengesJar: File = (file("../target").listFiles()?.toList() ?: emptyList())
.firstOrNull {
it.name.startsWith("Challenges-") && it.name.endsWith(".jar") &&
!it.name.contains("sources") && !it.name.contains("javadoc")
}
?: throw GradleException(
"Challenges jar not found in ../target — run 'mvn -q -DskipTests package' first."
)

plugwright {
minecraftVersion.set("1.21.11")
acceptEula.set(true)
// We deploy prebuilt jars (this is a Maven project, not a Gradle plugin build).
useExternalPluginsOnly.set(true)
testsDir.set(file("src/test/e2e"))
jvmArgs.set(listOf("-Xmx3G"))

writeFiles {
file("plugins/BentoBox.jar", bentoboxJar)
file("plugins/BentoBox/addons/BSkyBlock.jar", bskyblockJar)
file("plugins/BentoBox/addons/Challenges.jar", challengesJar)
}
}
Binary file added e2e/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
7 changes: 7 additions & 0 deletions e2e/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Loading
Loading