@thefehr/foundry-playwright 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/dist/auth.d.ts +18 -0
- package/dist/auth.js +287 -0
- package/dist/canvas.d.ts +47 -0
- package/dist/canvas.js +105 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +83 -0
- package/dist/cli/init.d.ts +5 -0
- package/dist/cli/init.js +129 -0
- package/dist/deprecations.d.ts +24 -0
- package/dist/deprecations.js +59 -0
- package/dist/docker.d.ts +37 -0
- package/dist/docker.js +140 -0
- package/dist/fixtures.d.ts +29 -0
- package/dist/fixtures.js +112 -0
- package/dist/helpers.d.ts +100 -0
- package/dist/helpers.js +414 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/setup/base.d.ts +97 -0
- package/dist/setup/base.js +53 -0
- package/dist/setup/index.d.ts +14 -0
- package/dist/setup/index.js +126 -0
- package/dist/setup/v13.d.ts +28 -0
- package/dist/setup/v13.js +308 -0
- package/dist/setup/v14.d.ts +31 -0
- package/dist/setup/v14.js +421 -0
- package/dist/state.d.ts +139 -0
- package/dist/state.js +321 -0
- package/dist/systems/base.d.ts +48 -0
- package/dist/systems/base.js +57 -0
- package/dist/systems/dnd5e.d.ts +27 -0
- package/dist/systems/dnd5e.js +30 -0
- package/dist/systems/index.d.ts +13 -0
- package/dist/systems/index.js +20 -0
- package/dist/systems/pf2e.d.ts +25 -0
- package/dist/systems/pf2e.js +62 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.js +11 -0
- package/dist/ui/base.d.ts +35 -0
- package/dist/ui/base.js +43 -0
- package/dist/ui/dnd5e.d.ts +8 -0
- package/dist/ui/dnd5e.js +10 -0
- package/dist/ui/index.d.ts +45 -0
- package/dist/ui/index.js +72 -0
- package/dist/ui/tidy5e.d.ts +11 -0
- package/dist/ui/tidy5e.js +30 -0
- package/package.json +67 -0
package/dist/ui/base.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default UI adapter for standard Foundry VTT interface.
|
|
3
|
+
*/
|
|
4
|
+
export class DefaultUIAdapter {
|
|
5
|
+
id = "default";
|
|
6
|
+
getActorSheetSelector() {
|
|
7
|
+
return ".window-app.sheet.actor";
|
|
8
|
+
}
|
|
9
|
+
async switchAppTab(page, appSelector, tabName) {
|
|
10
|
+
const app = page.locator(appSelector);
|
|
11
|
+
// Support for both V1 (nav.tabs a.item) and V2 (nav.tabs [data-tab])
|
|
12
|
+
const tabSelectors = [
|
|
13
|
+
`nav.tabs [data-tab]:has-text("${tabName}")`,
|
|
14
|
+
`nav.tabs [data-action="tab"]:has-text("${tabName}")`,
|
|
15
|
+
`nav.tabs a.item:has-text("${tabName}")`,
|
|
16
|
+
`[data-tab]:has-text("${tabName}")`,
|
|
17
|
+
];
|
|
18
|
+
let clicked = false;
|
|
19
|
+
for (const selector of tabSelectors) {
|
|
20
|
+
const candidate = app.locator(selector).first();
|
|
21
|
+
if ((await candidate.count()) > 0 && (await candidate.isVisible())) {
|
|
22
|
+
await candidate.click();
|
|
23
|
+
clicked = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!clicked) {
|
|
28
|
+
throw new Error(`Could not find tab "${tabName}" in application ${appSelector}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async handleCollapsibleSection(page, appSelector, sectionName) {
|
|
32
|
+
const app = page.locator(appSelector);
|
|
33
|
+
const section = app.locator(".form-group, .section, details").filter({ hasText: sectionName });
|
|
34
|
+
// Core Foundry doesn't have many collapsible sections by default,
|
|
35
|
+
// but we'll check for 'details' or custom classes
|
|
36
|
+
const details = section.locator("details").first();
|
|
37
|
+
if ((await details.count()) > 0) {
|
|
38
|
+
const isOpen = await details.evaluate((el) => el.open);
|
|
39
|
+
if (!isOpen)
|
|
40
|
+
await details.locator("summary").click();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/ui/dnd5e.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DefaultUIAdapter } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* UI adapter for the standard Dungeons & Dragons Fifth Edition sheets.
|
|
4
|
+
*/
|
|
5
|
+
export class DnD5eUIAdapter extends DefaultUIAdapter {
|
|
6
|
+
id = "dnd5e";
|
|
7
|
+
getActorSheetSelector() {
|
|
8
|
+
return "foundry-app, .window-app.dnd5e.sheet.actor, [id^='dnd5e-actor-'], [id^='actor-character-']";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Page } from "@playwright/test";
|
|
2
|
+
import { UIAdapter } from "./base.js";
|
|
3
|
+
/**
|
|
4
|
+
* Gets a UI adapter by its ID.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getUIAdapter(id: string): UIAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* Registers a new UI adapter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function registerUIAdapter(adapter: UIAdapter): void;
|
|
11
|
+
/**
|
|
12
|
+
* Provides methods for interacting with the Foundry VTT UI.
|
|
13
|
+
*/
|
|
14
|
+
export declare class FoundryUI {
|
|
15
|
+
private page;
|
|
16
|
+
private adapter;
|
|
17
|
+
constructor(page: Page, adapterId?: string);
|
|
18
|
+
/**
|
|
19
|
+
* Sets the UI adapter to use.
|
|
20
|
+
*/
|
|
21
|
+
setAdapter(adapterId: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Gets the CSS selector for actor sheets.
|
|
24
|
+
*/
|
|
25
|
+
getActorSheetSelector(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Switches to a specific tab on an application.
|
|
28
|
+
*/
|
|
29
|
+
switchTab(appSelector: string, tabName: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Switches to a specific tab on an actor sheet.
|
|
32
|
+
*/
|
|
33
|
+
switchActorTab(actorName: string, tabName: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Expands a collapsible section if it is currently collapsed.
|
|
36
|
+
*/
|
|
37
|
+
handleCollapsibleSection(appSelector: string, sectionName: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Simulates a drag-and-drop event in Foundry VTT.
|
|
40
|
+
*/
|
|
41
|
+
simulateDrop(targetSelector: string, data: any): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export * from "./base.js";
|
|
44
|
+
export * from "./tidy5e.js";
|
|
45
|
+
export * from "./dnd5e.js";
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DefaultUIAdapter } from "./base.js";
|
|
2
|
+
import { Tidy5eUIAdapter } from "./tidy5e.js";
|
|
3
|
+
import { DnD5eUIAdapter } from "./dnd5e.js";
|
|
4
|
+
const adapters = {
|
|
5
|
+
default: new DefaultUIAdapter(),
|
|
6
|
+
"tidy5e-sheet": new Tidy5eUIAdapter(),
|
|
7
|
+
dnd5e: new DnD5eUIAdapter(),
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Gets a UI adapter by its ID.
|
|
11
|
+
*/
|
|
12
|
+
export function getUIAdapter(id) {
|
|
13
|
+
return adapters[id] || adapters["default"];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Registers a new UI adapter.
|
|
17
|
+
*/
|
|
18
|
+
export function registerUIAdapter(adapter) {
|
|
19
|
+
adapters[adapter.id] = adapter;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Provides methods for interacting with the Foundry VTT UI.
|
|
23
|
+
*/
|
|
24
|
+
export class FoundryUI {
|
|
25
|
+
page;
|
|
26
|
+
adapter;
|
|
27
|
+
constructor(page, adapterId = "default") {
|
|
28
|
+
this.page = page;
|
|
29
|
+
this.adapter = getUIAdapter(adapterId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sets the UI adapter to use.
|
|
33
|
+
*/
|
|
34
|
+
setAdapter(adapterId) {
|
|
35
|
+
this.adapter = getUIAdapter(adapterId);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Gets the CSS selector for actor sheets.
|
|
39
|
+
*/
|
|
40
|
+
getActorSheetSelector() {
|
|
41
|
+
return this.adapter.getActorSheetSelector();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Switches to a specific tab on an application.
|
|
45
|
+
*/
|
|
46
|
+
async switchTab(appSelector, tabName) {
|
|
47
|
+
await this.adapter.switchAppTab(this.page, appSelector, tabName);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Switches to a specific tab on an actor sheet.
|
|
51
|
+
*/
|
|
52
|
+
async switchActorTab(actorName, tabName) {
|
|
53
|
+
const selector = `${this.adapter.getActorSheetSelector()}:has-text("${actorName}")`;
|
|
54
|
+
await this.switchTab(selector, tabName);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Expands a collapsible section if it is currently collapsed.
|
|
58
|
+
*/
|
|
59
|
+
async handleCollapsibleSection(appSelector, sectionName) {
|
|
60
|
+
await this.adapter.handleCollapsibleSection(this.page, appSelector, sectionName);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Simulates a drag-and-drop event in Foundry VTT.
|
|
64
|
+
*/
|
|
65
|
+
async simulateDrop(targetSelector, data) {
|
|
66
|
+
const { simulateFoundryDrop } = await import("../helpers.js");
|
|
67
|
+
await simulateFoundryDrop(this.page, targetSelector, data);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export * from "./base.js";
|
|
71
|
+
export * from "./tidy5e.js";
|
|
72
|
+
export * from "./dnd5e.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Page } from "@playwright/test";
|
|
2
|
+
import { DefaultUIAdapter } from "./base.js";
|
|
3
|
+
/**
|
|
4
|
+
* UI adapter for the Tidy 5e Sheets module.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Tidy5eUIAdapter extends DefaultUIAdapter {
|
|
7
|
+
id: string;
|
|
8
|
+
getActorSheetSelector(): string;
|
|
9
|
+
switchAppTab(page: Page, appSelector: string, tabName: string): Promise<void>;
|
|
10
|
+
handleCollapsibleSection(page: Page, appSelector: string, sectionName: string): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { expect } from "@playwright/test";
|
|
2
|
+
import { DefaultUIAdapter } from "./base.js";
|
|
3
|
+
/**
|
|
4
|
+
* UI adapter for the Tidy 5e Sheets module.
|
|
5
|
+
*/
|
|
6
|
+
export class Tidy5eUIAdapter extends DefaultUIAdapter {
|
|
7
|
+
id = "tidy5e-sheet";
|
|
8
|
+
getActorSheetSelector() {
|
|
9
|
+
return ".tidy5e-sheet.actor, .tidy5e-sheet.group";
|
|
10
|
+
}
|
|
11
|
+
async switchAppTab(page, appSelector, tabName) {
|
|
12
|
+
const app = page.locator(appSelector);
|
|
13
|
+
// Tidy5e might use different navigation or tab structures, especially for Group actors
|
|
14
|
+
const tabItem = app
|
|
15
|
+
.locator(".tidy-tabs [data-tab], nav.tabs a.item, .navigation .item")
|
|
16
|
+
.filter({ hasText: tabName })
|
|
17
|
+
.first();
|
|
18
|
+
await tabItem.evaluate((el) => el.click());
|
|
19
|
+
// In Tidy5e, the active state might be checked differently, but usually it's still a class
|
|
20
|
+
await expect(tabItem).toHaveClass(/active|selected/);
|
|
21
|
+
}
|
|
22
|
+
async handleCollapsibleSection(page, appSelector, sectionName) {
|
|
23
|
+
const app = page.locator(appSelector);
|
|
24
|
+
const section = app.locator(".tidy-collapsible").filter({ hasText: sectionName });
|
|
25
|
+
const isCollapsed = await section.evaluate((el) => el.classList.contains("collapsed"));
|
|
26
|
+
if (isCollapsed) {
|
|
27
|
+
await section.locator(".tidy-collapsible-header").click();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thefehr/foundry-playwright",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "A robust, multi-version E2E testing library for FoundryVTT modules and systems, powered by Playwright.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"e2e",
|
|
7
|
+
"foundryvtt",
|
|
8
|
+
"playwright",
|
|
9
|
+
"testing"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/TheFehr/foundry-playwright#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/TheFehr/foundry-playwright/issues"
|
|
14
|
+
},
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"author": "TheFehr",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/TheFehr/foundry-playwright.git"
|
|
20
|
+
},
|
|
21
|
+
"bin": {
|
|
22
|
+
"foundry-playwright": "./dist/cli/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"test": "vitest run --passWithNoTests",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:e2e": "playwright test",
|
|
35
|
+
"lint": "oxlint .",
|
|
36
|
+
"format": "oxfmt . --check",
|
|
37
|
+
"format:fix": "oxfmt .",
|
|
38
|
+
"verify:local": "tsx scripts/verify-local.ts",
|
|
39
|
+
"release": "release-it",
|
|
40
|
+
"prepublishOnly": "npm run build",
|
|
41
|
+
"prepare": "husky"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"commander": "^14.0.3"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@playwright/test": "^1.40.0",
|
|
48
|
+
"@release-it/conventional-changelog": "^11.0.0",
|
|
49
|
+
"@types/node": "^20.10.0",
|
|
50
|
+
"dotenv": "^17.4.2",
|
|
51
|
+
"husky": "^9.1.7",
|
|
52
|
+
"lint-staged": "^17.0.2",
|
|
53
|
+
"oxfmt": "^0.48.0",
|
|
54
|
+
"oxlint": "^1.63.0",
|
|
55
|
+
"release-it": "^20.0.1",
|
|
56
|
+
"tsx": "^4.21.0",
|
|
57
|
+
"typescript": "^5.3.0",
|
|
58
|
+
"vitest": "^1.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"@playwright/test": "^1.40.0"
|
|
62
|
+
},
|
|
63
|
+
"lint-staged": {
|
|
64
|
+
"**/*.{ts,json,js,md,yml}": "npm run format:fix",
|
|
65
|
+
"{src,e2e,scripts}/**/*.{ts,js}": "oxlint"
|
|
66
|
+
}
|
|
67
|
+
}
|