Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Build runner-package
# The example links all three by path, and npm cannot build a linked package: it packs
# the directory into a staging copy that never gets its dev dependencies.
- name: Build the local npm packages
run: |
cd runner-package
npm install
npm run build
npm run install:packages
npm run build:packages
- uses: drownek/plugwright-action@v1
with:
java-version: "17"
Expand Down
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ logs/
*.tsbuildinfo
dist/

# Test server runtime
# Test server runtime — plugwright writes it under <testsDir>/generated/<environment>
generated/
run/
test-server/
**/server.properties
Expand All @@ -62,9 +63,6 @@ test-server/
**/spigot.jar
server.jar

# Compiled test files
**/e2e/dist/

# Temporary files
*.tmp
*.temp
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,22 @@ plugwright {

**2. Initialize the test folder:**

Run the init command to set up your test folder.
This will automatically generate your package.json, TypeScript configuration, and an example test in a chosen directory.
Run the init command to set up your test folder. It asks where to put it, then writes an npm project with a `package.json`, a TypeScript config, a `.gitignore`, an example spec and an example runner plugin:

This command is interactive, so simply follow the prompts on your screen:
```bash
./gradlew plugwrightInit
```

```
src/test/e2e/
tests/example.spec.ts your specs go here
plugins/example-plugin.ts hooks, fixtures and matchers
package.json, tsconfig.json
.gitignore node_modules, dist, generated
```

Compiled specs land in `dist`, and everything an environment writes — the Paper server the local one starts, for instance — in `generated`. Neither belongs in version control. See [Project Layout](https://plugwright.dev/project-layout).

**3. Run your tests:**

```bash
Expand Down Expand Up @@ -131,6 +139,7 @@ plugwright {

`./gradlew plugwrightTest` runs the matrix and prints a summary per environment; `./gradlew plugwrightTestStaging` runs one. A server behind a login wall needs a runner plugin to get past it, and `@plugwright/auth-authme` is the reference implementation for AuthMe-style login. Writing your own kind of environment — a proxy, a Compose stack — is a Kotlin mode plus an npm package.

- [Project layout](https://plugwright.dev/project-layout) — where specs, plugins and generated files live
- [Environments](https://plugwright.dev/environments) — modes, tasks, the matrix
- [External servers](https://plugwright.dev/external-servers) — console channels, account pools, cleanup
- [Runner plugins](https://plugwright.dev/plugins) — hooks, fixtures, matchers, inherited tests
Expand Down
3 changes: 3 additions & 0 deletions auth-authme-package/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion auth-authme-package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"types": "dist/index.d.ts",
"scripts": {
"build": "rimraf dist && tsc",
"prepare": "npm run build",
"prepublishOnly": "npm run build",
"watch": "tsc --watch",
"typecheck": "tsc --noEmit"
Expand Down
3 changes: 3 additions & 0 deletions console-rcon-package/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion console-rcon-package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"types": "dist/index.d.ts",
"scripts": {
"build": "rimraf dist && tsc",
"prepare": "npm run build",
"prepublishOnly": "npm run build",
"watch": "tsc --watch",
"typecheck": "tsc --noEmit"
Expand Down
21 changes: 10 additions & 11 deletions docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ plugwright {
environments {
create("local", LocalMode) {
minecraftVersion.set("1.21.11")
runDir.set(file("run"))
acceptEula.set(true)
}
}
Expand All @@ -40,10 +39,11 @@ plugwright {
// ---- Server Configuration ----
// Version of Paper server to download and run
minecraftVersion.set("1.19.4")

// Directory where the test server will be located
runDir.set(file("run"))


// Where the test server lives. Leave it out and it goes to
// <testsDir>/generated/<environment>/run
// runDir.set(file("/mnt/fast-disk/paper"))

// Automatically accept the Minecraft EULA
acceptEula.set(true)

Expand Down Expand Up @@ -86,21 +86,20 @@ minecraftVersion.set("1.20.1")
```

<ParamField path="runDir" type="DirectoryProperty">
Directory where the test server will be located. Default is `project.layout.projectDirectory.dir("run")`.
Directory where the test server will be located. Unset by default, which puts it in `<testsDir>/generated/<environment>/run` — set it only to keep the server somewhere else. See [Project Layout](/project-layout).
</ParamField>

```kotlin
runDir.set(file("run"))
runDir.set(file("test-server"))
runDir.set(file("/mnt/fast-disk/paper"))
```

<ParamField path="testsDir" type="DirectoryProperty">
Directory containing test files. Default is `file("src/test/e2e")`.
Root of the test workspace: the npm project, the `tests` and `plugins` sources, and the `dist` and `generated` directories the build writes. Default is `file("src/test/e2e")`.
</ParamField>

```kotlin
testsDir.set(file("src/test/e2e"))
testsDir.set(file("tests/integration"))
testsDir.set(file("e2e"))
```

<ParamField path="acceptEula" type="Property<Boolean>">
Expand Down Expand Up @@ -237,7 +236,7 @@ Per-environment, inside `create(...) { }`:
</ParamField>

<ParamField path="plugins" type="Action<PluginsSpec>">
Runner plugins this environment loads: `npm("@scope/name") { options["key"] = "value" }` for a published package, `local(file("…"))` for a compiled file in your test project. See [Runner Plugins](/plugins).
Runner plugins this environment loads: `npm("@scope/name") { options["key"] = "value" }` for a published package, `local("name")` for one of your own in the workspace's `plugins` directory. See [Runner Plugins](/plugins).
</ParamField>

```kotlin
Expand Down
16 changes: 16 additions & 0 deletions docs/custom-modes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ What each piece is for:
- `serialize` writes `environment.config` at configuration time. Secrets stay `SecretRef`s here — `node.put("password", spec.password.get())` writes a reference, not a password.
- `registerTasks` adds tasks named `plugwright<Suffix><Environment>`, so `register("Up", ...)` in an environment called `proxy` gives `plugwrightUpProxy`. `prepareTask` marks the one that has to run before the tests do.

### Files your mode generates

Anything written while an environment runs belongs under `ctx.layout.generatedDir(ctx.environmentName)` — `src/test/e2e/generated/proxy` for the mode above. That directory is gitignored and is yours alone; no other environment writes there.

If the spec has a property for it, fill the default in `applyLayoutDefaults` rather than in the property's convention. It runs before validation, only for properties the build script left unset, so an explicit value in the build script still wins:

```kotlin
override fun applyLayoutDefaults(spec: VelocityEnvironmentSpec, layout: PlugwrightLayout) {
if (!spec.workDir.isPresent) {
spec.workDir.set(File(layout.generatedDir(spec.name), "compose"))
}
}
```

`PlugwrightLayout` also knows where the sources and the compiled output are: `testsDir`, `pluginsDir`, `compiledTestsDir`, `compiledPluginsDir`. See [Project Layout](/project-layout).

Preparation belongs in a task rather than a callback. A callback executed inside someone else's `@TaskAction` drags your mode object into that task's state, breaks the configuration cache, and can never be run on its own. A task with declared inputs and outputs gets up-to-date checks and a name someone can type.

If a config value needs something only a task can reach — the Java toolchain, a Gradle service — set it from `registerTasks` with `ctx.environmentConfig(provider)` instead of from `serialize`. That is what `LocalMode` does for the Java executable path.
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"pages": [
"introduction",
"quickstart",
"project-layout",
"configuration"
]
},
Expand Down
3 changes: 1 addition & 2 deletions docs/environments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ plugwright {
create("local", LocalMode) {
minecraftVersion.set("1.21.11")
acceptEula.set(true)
runDir.set(file("run"))
}

create("staging", ExternalMode) {
Expand All @@ -38,7 +37,7 @@ plugwright {
}
```

The name you pass to `create` becomes the task suffix and the report file name: `local` gives you `plugwrightTestLocal` and `build/reports/plugwright/local.json`.
The name you pass to `create` becomes the task suffix and the report file name: `local` gives you `plugwrightTestLocal` and `build/reports/plugwright/local.json`. It also names the directory the environment writes to — `src/test/e2e/generated/local`, where the Paper server for that environment ends up. Two local environments in one build therefore run two separate servers without either one saying where. See [Project Layout](/project-layout).

<Note>
A build script with no `environments { }` block still works. The flat properties (`minecraftVersion`, `runDir`, `downloadPlugins`, and the rest) describe one implicit `local` environment, exactly as they did before. See [Configuration](/configuration).
Expand Down
6 changes: 3 additions & 3 deletions docs/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ create("staging", ExternalMode) {
npm("@plugwright/auth-authme") {
options["loginCommand"] = "/log"
}
local(file("src/test/e2e/dist/plugins/staging.js")) {
local("staging") {
inheritTests = false
}
}
}
```

`npm(...)` names a published package, installed by `plugwrightCompileTests` along with the rest of the environment's packages. `local(...)` points at a compiled file in your own test project. Options are plain strings — anything secret belongs in `accounts { }`, where it stays a secret reference.
`npm(...)` names a published package, installed by `plugwrightCompileTests` along with the rest of the environment's packages. `local(...)` names a plugin of your own: `local("staging")` is `plugins/staging.ts` in the test workspace, compiled to `dist/plugins/staging.js` by the same `tsc` run as your specs. For a plugin that lives outside the workspace there is still `local(file("..."))`. Options are plain strings — anything secret belongs in `accounts { }`, where it stays a secret reference.

`LocalMode` takes the same block. A local server running an authentication plugin needs the login hook exactly as much as a remote one does.

Expand Down Expand Up @@ -82,7 +82,7 @@ tests: [

`preflight` tests run before any user spec and abort the run when they fail — there is no point testing a shop when nobody can log in. `suite` tests run alongside your own and are tagged with the plugin's name in the report.

Spec discovery skips `node_modules`, so this is the only way a packaged test ever runs. Per-plugin, `inheritTests = false` loads the hooks and matchers without the tests.
Spec discovery only looks at your own compiled `tests` directory, so this is the only way a packaged test ever runs. Per-plugin, `inheritTests = false` loads the hooks and matchers without the tests.

## Fixtures

Expand Down
82 changes: 82 additions & 0 deletions docs/project-layout.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
title: "Project Layout"
description: "Where the specs, the plugins and the generated files live."
---

Everything plugwright needs sits under one directory — `src/test/e2e` unless you point `testsDir` somewhere else. It is an npm project, so `package.json` and `node_modules` are there too:

```
src/test/e2e/
package.json the npm project the runner is installed into
tsconfig.json
.gitignore node_modules, dist, generated
tests/ your specs
shop.spec.ts
plugins/ runner plugins you wrote yourself
stand-reset.ts
dist/ compiled output, mirroring tests/ and plugins/
generated/ what the environments write while they run
local/run/ the Paper server the local environment starts
node_modules/
```

Three of those directories are disposable: `node_modules`, `dist` and `generated`. Delete any of them and the next `plugwrightTest` recreates it. `plugwrightInit` writes a `.gitignore` covering all three; if you already have one, it appends the lines it needs and leaves the rest alone.

## tests

`plugwrightCompileTests` compiles `tests/**/*.ts` into `dist/tests`, keeping subdirectories, and the runner scans the result for `.spec.js`. Group specs into folders however you like — `tests/economy/shop.spec.ts` is fine.

A workspace of plain JavaScript needs no compile step. Without a `tsconfig.json` the runner reads `tests/` directly.

## plugins

Runner plugins — hooks, fixtures, matchers, inherited tests — go in `plugins/`, one file each, and compile into `dist/plugins`. A plugin is loaded by name:

```kotlin
plugins {
local("stand-reset") // plugins/stand-reset.ts
}
```

`local(file(...))` still takes a path, for a plugin that lives somewhere else entirely. See [Runner Plugins](/plugins).

## generated

Each environment gets its own directory under `generated/`, named after it. The local environment puts its Paper server in `generated/<environment>/run`: the jar, the worlds, the logs, the plugins it downloaded. Two local environments in one matrix therefore never share a server directory.

You can still choose the directory yourself, and an explicit value always wins:

```kotlin
environments {
create("local", LocalMode) {
runDir.set(file("/mnt/fast-disk/paper"))
}
}
```

The `stand` in the [example project](https://github.com/Drownek/plugwright/tree/master/example_plugin) shows why the default is convenient: an external environment can point at the very server the local one left behind, because there is only one place it could be.

## Moving the whole thing

`testsDir` is the root of all of this:

```kotlin
plugwright {
testsDir.set(file("e2e"))
}
```

Then the specs are in `e2e/tests`, the server in `e2e/generated/local/run`, and so on.

## Migrating from the old layout

Before this layout, specs sat directly in `testsDir` and the local server went to a `run/` directory next to `build.gradle.kts`. The move is mostly automatic — the first `plugwrightCompileTests` after upgrading moves every spec it finds into `tests/`, subdirectories intact, and rewrites `tsconfig.json` so `include` points at the new place. It logs both.

Four things are worth checking by hand afterwards:

1. **Your `.gitignore`.** `run/` no longer needs an entry. `generated/` inside the workspace does — run `plugwrightInit` again to have the lines appended, or add them yourself.
2. **`runDir`.** A build script that sets it keeps that exact directory. Drop the line to get `generated/<environment>/run` instead, and move the server there if you want to keep the downloaded jar and the worlds.
3. **Local plugins.** `local(file("src/test/e2e/dist/plugins/x.js"))` becomes `local("x")` once the source is in `plugins/`.
4. **A `tsconfig.json` with comments.** JSON with comments is legal in a `tsconfig` and unparseable as JSON, so plugwright leaves such a file untouched and says so. Point `include` at `tests/**/*.ts` and `plugins/**/*.ts` yourself.

If you would rather do the move by hand, `git mv` the specs into `tests/` before upgrading. The migration only runs while there is no `tests/` directory at all.
15 changes: 11 additions & 4 deletions docs/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,21 @@ description: "Start running your first test in less than 5 minutes."
</Step>

<Step title="Initialization">
Run the init command to set up your test folder.
This will automatically generate your package.json, TypeScript configuration, and an example test in a chosen directory.

This command is interactive, so simply follow the prompts on your screen:
Run the init command to set up your test folder. It asks where to put it, then writes an npm project with a `package.json`, a TypeScript config, a `.gitignore`, an example spec and an example runner plugin:

```bash
./gradlew plugwrightInit
```

```
src/test/e2e/
tests/example.spec.ts your specs go here
plugins/example-plugin.ts hooks, fixtures and matchers
package.json, tsconfig.json
.gitignore node_modules, dist, generated
```

Everything a run generates — the compiled specs, the server the local environment starts — stays inside that directory, under `dist` and `generated`. See [Project Layout](/project-layout).
</Step>

<Step title="Your First Test">
Expand Down
2 changes: 1 addition & 1 deletion docs/writing-tests.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test('test description', async ({ player }) => {

## Your First Test

Create `src/test/e2e/first.spec.ts`:
Create `src/test/e2e/tests/first.spec.ts` — specs live in `tests`, in whatever subdirectories you like ([Project Layout](/project-layout)):

```typescript
import { test, expect } from '@drownek/plugwright';
Expand Down
Loading