@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.
Files changed (48) hide show
  1. package/README.md +79 -0
  2. package/dist/auth.d.ts +18 -0
  3. package/dist/auth.js +287 -0
  4. package/dist/canvas.d.ts +47 -0
  5. package/dist/canvas.js +105 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.js +83 -0
  8. package/dist/cli/init.d.ts +5 -0
  9. package/dist/cli/init.js +129 -0
  10. package/dist/deprecations.d.ts +24 -0
  11. package/dist/deprecations.js +59 -0
  12. package/dist/docker.d.ts +37 -0
  13. package/dist/docker.js +140 -0
  14. package/dist/fixtures.d.ts +29 -0
  15. package/dist/fixtures.js +112 -0
  16. package/dist/helpers.d.ts +100 -0
  17. package/dist/helpers.js +414 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.js +9 -0
  20. package/dist/setup/base.d.ts +97 -0
  21. package/dist/setup/base.js +53 -0
  22. package/dist/setup/index.d.ts +14 -0
  23. package/dist/setup/index.js +126 -0
  24. package/dist/setup/v13.d.ts +28 -0
  25. package/dist/setup/v13.js +308 -0
  26. package/dist/setup/v14.d.ts +31 -0
  27. package/dist/setup/v14.js +421 -0
  28. package/dist/state.d.ts +139 -0
  29. package/dist/state.js +321 -0
  30. package/dist/systems/base.d.ts +48 -0
  31. package/dist/systems/base.js +57 -0
  32. package/dist/systems/dnd5e.d.ts +27 -0
  33. package/dist/systems/dnd5e.js +30 -0
  34. package/dist/systems/index.d.ts +13 -0
  35. package/dist/systems/index.js +20 -0
  36. package/dist/systems/pf2e.d.ts +25 -0
  37. package/dist/systems/pf2e.js +62 -0
  38. package/dist/types/index.d.ts +18 -0
  39. package/dist/types/index.js +11 -0
  40. package/dist/ui/base.d.ts +35 -0
  41. package/dist/ui/base.js +43 -0
  42. package/dist/ui/dnd5e.d.ts +8 -0
  43. package/dist/ui/dnd5e.js +10 -0
  44. package/dist/ui/index.d.ts +45 -0
  45. package/dist/ui/index.js +72 -0
  46. package/dist/ui/tidy5e.d.ts +11 -0
  47. package/dist/ui/tidy5e.js +30 -0
  48. package/package.json +67 -0
package/dist/state.js ADDED
@@ -0,0 +1,321 @@
1
+ import { UserRole } from "./types/index.js";
2
+ import { getSystemStateAdapter } from "./systems/index.js";
3
+ /**
4
+ * Provides methods for direct manipulation of the Foundry VTT state.
5
+ */
6
+ export class FoundryState {
7
+ page;
8
+ systemId;
9
+ deprecationTracker;
10
+ adapter;
11
+ constructor(page, systemId = "dnd5e", deprecationTracker) {
12
+ this.page = page;
13
+ this.systemId = systemId;
14
+ this.deprecationTracker = deprecationTracker;
15
+ this.adapter = getSystemStateAdapter(systemId, page);
16
+ }
17
+ /**
18
+ * Sets the system adapter to use.
19
+ */
20
+ setSystem(systemId) {
21
+ this.systemId = systemId;
22
+ this.adapter = getSystemStateAdapter(systemId, this.page);
23
+ }
24
+ /**
25
+ * Aggressively removes properties known to trigger deprecation warnings on access.
26
+ * This is used before returning data from page.evaluate.
27
+ */
28
+ static get SanitizerScript() {
29
+ return `(obj) => {
30
+ if (!obj || typeof obj !== 'object') return obj;
31
+ const deprecatedDnD5e = ['darkvision', 'blindsight', 'tremorsense', 'truesight', 'special'];
32
+ const cleanSenses = (o) => {
33
+ if (!o || typeof o !== 'object') return o;
34
+ const result = Array.isArray(o) ? [] : {};
35
+ for (let key in o) {
36
+ if (key === 'senses') {
37
+ const senses = o[key];
38
+ const cleanS = Array.isArray(senses) ? [] : {};
39
+ for (let skey in senses) {
40
+ if (deprecatedDnD5e.includes(skey)) continue;
41
+ cleanS[skey] = senses[skey];
42
+ }
43
+ result[key] = cleanS;
44
+ } else {
45
+ result[key] = cleanSenses(o[key]);
46
+ }
47
+ }
48
+ return result;
49
+ };
50
+ return cleanSenses(obj);
51
+ }`;
52
+ }
53
+ /**
54
+ * Creates a new Foundry VTT document.
55
+ * @param documentName The type of document (e.g., "Actor", "Item").
56
+ * @param data The document data.
57
+ */
58
+ async createDocument(documentName, data) {
59
+ return this.page.evaluate(async ({ documentName, data, sanitizer }) => {
60
+ // @ts-ignore
61
+ const cls = CONFIG[documentName].documentClass;
62
+ const doc = await cls.create(data);
63
+ if (!doc)
64
+ return null;
65
+ // Use raw _source to avoid getters/deprecations
66
+ const obj = doc._source ? JSON.parse(JSON.stringify(doc._source)) : doc.toObject();
67
+ const sanitize = new Function(`return ${sanitizer}`)();
68
+ return sanitize(obj);
69
+ }, { documentName, data, sanitizer: FoundryState.SanitizerScript });
70
+ }
71
+ /**
72
+ * Updates an existing document in Foundry VTT.
73
+ * @param documentName The name of the document type.
74
+ * @param id The ID of the document to update.
75
+ * @param delta The data to update.
76
+ */
77
+ async updateDocument(documentName, id, delta) {
78
+ return this.page.evaluate(({ documentName, id, delta }) => {
79
+ // @ts-ignore
80
+ const doc = game.collections.get(documentName).get(id);
81
+ return doc.update(delta);
82
+ }, { documentName, id, delta });
83
+ }
84
+ /**
85
+ * Deletes a document in Foundry VTT.
86
+ * @param documentName The name of the document type.
87
+ * @param id The ID of the document to delete.
88
+ */
89
+ async deleteDocument(documentName, id) {
90
+ return this.page.evaluate(({ documentName, id }) => {
91
+ // @ts-ignore
92
+ const doc = game.collections.get(documentName).get(id);
93
+ return doc.delete();
94
+ }, { documentName, id });
95
+ }
96
+ /**
97
+ * Gets a document by its ID.
98
+ * @param documentName The name of the document type.
99
+ * @param id The ID of the document.
100
+ */
101
+ async getDocument(documentName, id) {
102
+ return this.page.evaluate(({ documentName, id, sanitizer }) => {
103
+ // @ts-ignore
104
+ const doc = game.collections.get(documentName).get(id);
105
+ if (!doc)
106
+ return null;
107
+ // Use raw _source to avoid getters/deprecations
108
+ const obj = doc._source ? JSON.parse(JSON.stringify(doc._source)) : doc.toObject();
109
+ const sanitize = new Function(`return ${sanitizer}`)();
110
+ return sanitize(obj);
111
+ }, { documentName, id, sanitizer: FoundryState.SanitizerScript });
112
+ }
113
+ /**
114
+ * Gets a document by its name.
115
+ * @param documentName The name of the document type.
116
+ * @param name The name of the document.
117
+ */
118
+ async getDocumentByName(documentName, name) {
119
+ return this.page.evaluate(({ documentName, name, sanitizer }) => {
120
+ const g = window.game;
121
+ const collection = g.collections.get(documentName) || g[documentName.toLowerCase() + "s"];
122
+ const doc = collection.getName(name);
123
+ if (!doc)
124
+ return null;
125
+ // Use raw _source to avoid getters/deprecations
126
+ const obj = doc._source ? JSON.parse(JSON.stringify(doc._source)) : doc.toObject();
127
+ const sanitize = new Function(`return ${sanitizer}`)();
128
+ return sanitize(obj);
129
+ }, { documentName, name, sanitizer: FoundryState.SanitizerScript });
130
+ }
131
+ /**
132
+ * Creates a new User.
133
+ */
134
+ async createUser(name, role = UserRole.PLAYER, password) {
135
+ return this.page.evaluate(({ name, role, password }) => {
136
+ // @ts-ignore
137
+ return User.create({ name, role, password });
138
+ }, { name, role, password });
139
+ }
140
+ /**
141
+ * Sets a user's role.
142
+ */
143
+ async setUserRole(userId, role) {
144
+ return this.page.evaluate(({ userId, role }) => {
145
+ // @ts-ignore
146
+ const user = game.users.get(userId);
147
+ if (!user)
148
+ throw new Error(`User not found: ${userId}`);
149
+ return user.update({ role });
150
+ }, { userId, role });
151
+ }
152
+ /**
153
+ * Configures a specific permission for a user role.
154
+ */
155
+ async setRolePermission(permission, role, allowed) {
156
+ return this.page.evaluate(({ permission, role, allowed }) => {
157
+ // @ts-ignore
158
+ const current = game.settings.get("core", "permissions") || {};
159
+ const update = { ...current, [permission]: { ...current[permission], [role]: allowed } };
160
+ // @ts-ignore
161
+ return game.settings.set("core", "permissions", update);
162
+ }, { permission, role, allowed });
163
+ }
164
+ /**
165
+ * Grants currency to an actor.
166
+ * @param actorName The name of the actor.
167
+ * @param amount The amount of currency to grant.
168
+ * @param currency The type of currency (e.g., "gp", "sp").
169
+ */
170
+ async grantCurrency(actorName, amount, currency = "gp") {
171
+ return this.adapter.grantCurrency(this.page, actorName, amount, currency);
172
+ }
173
+ /**
174
+ * Gets the verification parameters for a currency update.
175
+ */
176
+ getCurrencyVerifyParams(actorName, amount, currency = "gp") {
177
+ return this.adapter.getCurrencyVerifyParams(actorName, amount, currency);
178
+ }
179
+ /**
180
+ * Sets an actor's HP.
181
+ * @param actorName The name of the actor.
182
+ * @param value The new HP value.
183
+ * @param max The new max HP value (optional).
184
+ */
185
+ async setActorHP(actorName, value, max) {
186
+ return this.adapter.setActorHP(this.page, actorName, value, max);
187
+ }
188
+ /**
189
+ * Rolls a specific roll for an actor.
190
+ * @param actorName The name of the actor.
191
+ * @param formula The roll formula (e.g., "1d20 + 5").
192
+ * @param label A label for the roll.
193
+ */
194
+ async roll(actorName, formula, label = "Test Roll") {
195
+ return this.page.evaluate(({ actorName, formula, label }) => {
196
+ // @ts-ignore
197
+ const actor = game.actors.getName(actorName);
198
+ if (!actor)
199
+ throw new Error(`Actor not found: ${actorName}`);
200
+ // @ts-ignore
201
+ const roll = new Roll(formula, actor.getRollData());
202
+ return roll.toMessage({ flavor: label });
203
+ }, { actorName, formula, label });
204
+ }
205
+ /**
206
+ * Executes a macro by name.
207
+ * @param name The name of the macro.
208
+ * @param args Arguments to pass to the macro.
209
+ */
210
+ async executeMacro(name, ...args) {
211
+ return this.page.evaluate(({ name, args }) => {
212
+ // @ts-ignore
213
+ const macro = game.macros.getName(name);
214
+ return macro.execute(...args);
215
+ }, { name, args });
216
+ }
217
+ /**
218
+ * Manually triggers a Foundry VTT hook.
219
+ * @param hookName The name of the hook (e.g., "renderActorSheet").
220
+ * @param args Arguments to pass to the hook.
221
+ */
222
+ async triggerHook(hookName, ...args) {
223
+ return this.page.evaluate(({ hookName, args }) => {
224
+ return window.Hooks.call(hookName, ...args);
225
+ }, { hookName, args });
226
+ }
227
+ /**
228
+ * Emits a socket event via the Foundry VTT socket.
229
+ * @param eventName The name of the event.
230
+ * @param data The data to emit.
231
+ */
232
+ async emitSocket(eventName, data) {
233
+ return this.page.evaluate(({ eventName, data }) => {
234
+ return window.game.socket.emit(eventName, data);
235
+ }, { eventName, data });
236
+ }
237
+ /**
238
+ * Waits for a specific Foundry VTT hook to be called.
239
+ * @param hookName The name of the hook.
240
+ * @param timeout The timeout in milliseconds.
241
+ * @returns The first argument passed to the hook.
242
+ */
243
+ async waitForHook(hookName, timeout = 10000) {
244
+ await this.page.evaluate((hookName) => {
245
+ window._hookLogs = window._hookLogs || {};
246
+ window._hookLogs[hookName] = window._hookLogs[hookName] || [];
247
+ window.Hooks.on(hookName, (...args) => {
248
+ window._hookLogs[hookName].push(args);
249
+ });
250
+ }, hookName);
251
+ await this.page.waitForFunction((name) => {
252
+ return window._hookLogs?.[name]?.length > 0;
253
+ }, hookName, { timeout });
254
+ return this.page.evaluate((name) => {
255
+ const logs = window._hookLogs[name];
256
+ window._hookLogs[name] = [];
257
+ return logs[0]?.[0]; // Return the first argument of the first call
258
+ }, hookName);
259
+ }
260
+ /**
261
+ * Waits for a specific socket event to be received.
262
+ * @param eventName The name of the event.
263
+ * @param timeout The timeout in milliseconds.
264
+ */
265
+ async waitForSocket(eventName, timeout = 10000) {
266
+ await this.page.evaluate((eventName) => {
267
+ window._socketLogs = window._socketLogs || {};
268
+ window._socketLogs[eventName] = window._socketLogs[eventName] || 0;
269
+ window.game.socket.on(eventName, () => {
270
+ window._socketLogs[eventName]++;
271
+ });
272
+ }, eventName);
273
+ await this.page.waitForFunction((name) => {
274
+ return window._socketLogs?.[name] > 0;
275
+ }, eventName, { timeout });
276
+ return this.page.evaluate((name) => {
277
+ const count = window._socketLogs[name];
278
+ window._socketLogs[name] = 0;
279
+ return count;
280
+ }, eventName);
281
+ }
282
+ /**
283
+ * Assigns an actor to a user.
284
+ */
285
+ async assignActorToUser(userId, actorId) {
286
+ return this.page.evaluate(({ userId, actorId }) => {
287
+ // @ts-ignore
288
+ const user = game.users.get(userId);
289
+ if (!user)
290
+ throw new Error(`User not found: ${userId}`);
291
+ return user.update({ character: actorId });
292
+ }, { userId, actorId });
293
+ }
294
+ /**
295
+ * Updates an existing user.
296
+ */
297
+ async updateUser(userId, delta) {
298
+ return this.page.evaluate(({ userId, delta }) => {
299
+ // @ts-ignore
300
+ const user = game.users.get(userId);
301
+ if (!user)
302
+ throw new Error(`User not found: ${userId}`);
303
+ return user.update(delta);
304
+ }, { userId, delta });
305
+ }
306
+ /**
307
+ * Creates a test actor.
308
+ */
309
+ async createTestActor(name = "Test Actor") {
310
+ const { type, system } = this.adapter.getTestActorData(name);
311
+ return this.createDocument("Actor", { name, type, system });
312
+ }
313
+ /**
314
+ * Sets or updates a Foundry VTT setting.
315
+ */
316
+ async setSetting(module, key, value) {
317
+ return this.page.evaluate(({ module, key, value }) => {
318
+ return window.game.settings.set(module, key, value);
319
+ }, { module, key, value });
320
+ }
321
+ }
@@ -0,0 +1,48 @@
1
+ import { FoundryPage } from "../types/index.js";
2
+ /**
3
+ * Interface for system-specific state manipulation logic.
4
+ */
5
+ export interface SystemStateAdapter {
6
+ /** The system ID this adapter handles. */
7
+ readonly id: string;
8
+ /**
9
+ * Grants currency to an actor.
10
+ */
11
+ grantCurrency(page: FoundryPage, actorName: string, amount: number, currency: string): Promise<any>;
12
+ /**
13
+ * Provides the system-specific data structure for a test actor.
14
+ */
15
+ getTestActorData(name: string): {
16
+ type: string;
17
+ system: any;
18
+ };
19
+ /**
20
+ * Sets an actor's HP.
21
+ */
22
+ setActorHP(page: FoundryPage, actorName: string, value: number, max?: number): Promise<any>;
23
+ /**
24
+ * Returns the log key and a predicate to verify a currency update in verifyResult.
25
+ */
26
+ getCurrencyVerifyParams(actorName: string, amount: number, currency: string): {
27
+ key: string;
28
+ predicate: (data: any, extra?: any) => boolean;
29
+ };
30
+ }
31
+ /**
32
+ * Base implementation of SystemStateAdapter with default (often DnD5e-like) logic.
33
+ */
34
+ export declare abstract class BaseSystemStateAdapter implements SystemStateAdapter {
35
+ protected page?: FoundryPage | undefined;
36
+ abstract id: string;
37
+ constructor(page?: FoundryPage | undefined);
38
+ grantCurrency(page: FoundryPage, actorName: string, amount: number, currency: string): Promise<any>;
39
+ getTestActorData(_name: string): {
40
+ type: string;
41
+ system: any;
42
+ };
43
+ setActorHP(page: FoundryPage, actorName: string, value: number, max?: number): Promise<any>;
44
+ getCurrencyVerifyParams(_actorName: string, _amount: number, _currency: string): {
45
+ key: string;
46
+ predicate: (data: any, extra?: any) => boolean;
47
+ };
48
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Base implementation of SystemStateAdapter with default (often DnD5e-like) logic.
3
+ */
4
+ export class BaseSystemStateAdapter {
5
+ page;
6
+ constructor(page) {
7
+ this.page = page;
8
+ }
9
+ async grantCurrency(page, actorName, amount, currency) {
10
+ return page.evaluate(({ actorName, amount, currency }) => {
11
+ const actor = window.game.actors.getName(actorName);
12
+ if (!actor)
13
+ throw new Error(`Actor not found: ${actorName}`);
14
+ const current = (actor.system.currency?.[currency] || 0) + amount;
15
+ return actor.update({ [`system.currency.${currency}`]: current });
16
+ }, { actorName, amount, currency });
17
+ }
18
+ getTestActorData(_name) {
19
+ return {
20
+ type: "character",
21
+ system: {
22
+ attributes: { hp: { value: 10, max: 10 } },
23
+ },
24
+ };
25
+ }
26
+ async setActorHP(page, actorName, value, max) {
27
+ return page.evaluate(({ actorName, value, max }) => {
28
+ const actor = window.game.actors.getName(actorName);
29
+ if (!actor)
30
+ throw new Error(`Actor not found: ${actorName}`);
31
+ const update = { "system.attributes.hp.value": value };
32
+ if (max !== undefined)
33
+ update["system.attributes.hp.max"] = max;
34
+ return actor.update(update);
35
+ }, { actorName, value, max });
36
+ }
37
+ getCurrencyVerifyParams(_actorName, _amount, _currency) {
38
+ return {
39
+ key: "actor-update",
40
+ predicate: (data, extra) => {
41
+ // Broadly match any update that touches currency for the target actor
42
+ if (data.name !== extra.actorName)
43
+ return false;
44
+ // Deep check for the expected currency value in the delta
45
+ const delta = data.delta || {};
46
+ // Check for direct property update: "system.currency.gp": 100
47
+ if (delta[`system.currency.${extra.currency}`] === extra.amount)
48
+ return true;
49
+ // Check for nested update: { system: { currency: { gp: 100 } } }
50
+ const nestedVal = delta.system?.currency?.[extra.currency];
51
+ if (nestedVal === extra.amount)
52
+ return true;
53
+ return false;
54
+ },
55
+ };
56
+ }
57
+ }
@@ -0,0 +1,27 @@
1
+ import { FoundryPage } from "../types/index.js";
2
+ import { BaseSystemStateAdapter } from "./base.js";
3
+ /**
4
+ * State adapter for the D&D 5th Edition system.
5
+ */
6
+ export declare class DnD5eStateAdapter extends BaseSystemStateAdapter {
7
+ readonly id = "dnd5e";
8
+ constructor(page?: FoundryPage);
9
+ getTestActorData(_name: string): {
10
+ type: string;
11
+ system: {
12
+ details: {
13
+ senses: {
14
+ ranges: {
15
+ darkvision: number;
16
+ };
17
+ };
18
+ };
19
+ attributes: {
20
+ hp: {
21
+ value: number;
22
+ max: number;
23
+ };
24
+ };
25
+ };
26
+ };
27
+ }
@@ -0,0 +1,30 @@
1
+ import { BaseSystemStateAdapter } from "./base.js";
2
+ /**
3
+ * State adapter for the D&D 5th Edition system.
4
+ */
5
+ export class DnD5eStateAdapter extends BaseSystemStateAdapter {
6
+ id = "dnd5e";
7
+ constructor(page) {
8
+ super(page);
9
+ if (page?.deprecationTracker) {
10
+ page.deprecationTracker.registerFailure(["has moved to", "senses", "dnd5e"]);
11
+ }
12
+ }
13
+ getTestActorData(_name) {
14
+ return {
15
+ type: "character",
16
+ system: {
17
+ details: {
18
+ senses: {
19
+ ranges: {
20
+ darkvision: 60,
21
+ },
22
+ },
23
+ },
24
+ attributes: {
25
+ hp: { value: 10, max: 10 },
26
+ },
27
+ },
28
+ };
29
+ }
30
+ }
@@ -0,0 +1,13 @@
1
+ import { FoundryPage } from "../types/index.js";
2
+ import { SystemStateAdapter } from "./base.js";
3
+ /**
4
+ * Gets a system state adapter by its ID.
5
+ */
6
+ export declare function getSystemStateAdapter(id: string, page?: FoundryPage): SystemStateAdapter;
7
+ /**
8
+ * Initializes all known system adapters to register their deprecation patterns.
9
+ */
10
+ export declare function initAllSystems(page: FoundryPage): void;
11
+ export * from "./base.js";
12
+ export * from "./dnd5e.js";
13
+ export * from "./pf2e.js";
@@ -0,0 +1,20 @@
1
+ import { DnD5eStateAdapter } from "./dnd5e.js";
2
+ import { PF2eStateAdapter } from "./pf2e.js";
3
+ /**
4
+ * Gets a system state adapter by its ID.
5
+ */
6
+ export function getSystemStateAdapter(id, page) {
7
+ if (id === "pf2e")
8
+ return new PF2eStateAdapter(page);
9
+ return new DnD5eStateAdapter(page);
10
+ }
11
+ /**
12
+ * Initializes all known system adapters to register their deprecation patterns.
13
+ */
14
+ export function initAllSystems(page) {
15
+ new DnD5eStateAdapter(page);
16
+ new PF2eStateAdapter(page);
17
+ }
18
+ export * from "./base.js";
19
+ export * from "./dnd5e.js";
20
+ export * from "./pf2e.js";
@@ -0,0 +1,25 @@
1
+ import { FoundryPage } from "../types/index.js";
2
+ import { BaseSystemStateAdapter } from "./base.js";
3
+ /**
4
+ * State adapter for the Pathfinder 2nd Edition system.
5
+ */
6
+ export declare class PF2eStateAdapter extends BaseSystemStateAdapter {
7
+ readonly id = "pf2e";
8
+ constructor(page?: FoundryPage);
9
+ grantCurrency(page: FoundryPage, actorName: string, amount: number, currency: string): Promise<any>;
10
+ getTestActorData(_name: string): {
11
+ type: string;
12
+ system: {
13
+ attributes: {
14
+ hp: {
15
+ value: number;
16
+ max: number;
17
+ };
18
+ };
19
+ };
20
+ };
21
+ getCurrencyVerifyParams(actorName: string, amount: number, currency: string): {
22
+ key: string;
23
+ predicate: (data: any, extra?: any) => boolean;
24
+ };
25
+ }
@@ -0,0 +1,62 @@
1
+ import { BaseSystemStateAdapter } from "./base.js";
2
+ /**
3
+ * State adapter for the Pathfinder 2nd Edition system.
4
+ */
5
+ export class PF2eStateAdapter extends BaseSystemStateAdapter {
6
+ id = "pf2e";
7
+ constructor(page) {
8
+ super(page);
9
+ if (page?.deprecationTracker) {
10
+ page.deprecationTracker.registerIgnore(["template.json is deprecated"]);
11
+ }
12
+ }
13
+ async grantCurrency(page, actorName, amount, currency) {
14
+ return page.evaluate(async ({ actorName, amount, currency }) => {
15
+ const actor = window.game.actors.getName(actorName);
16
+ if (!actor)
17
+ throw new Error(`Actor not found: ${actorName}`);
18
+ // Create the treasure item
19
+ const [newItem] = await actor.createEmbeddedDocuments("Item", [
20
+ {
21
+ name: `${currency.toUpperCase()} Coins`,
22
+ type: "treasure",
23
+ system: {
24
+ denomination: currency,
25
+ quantity: amount,
26
+ },
27
+ },
28
+ ]);
29
+ // Definitively log to the verification registry
30
+ // @ts-ignore
31
+ if (window.FP_VERIFY) {
32
+ window.FP_VERIFY.log("pf2e-currency-added", {
33
+ actorName,
34
+ amount,
35
+ currency,
36
+ itemId: newItem.id,
37
+ });
38
+ }
39
+ return newItem;
40
+ }, { actorName, amount, currency });
41
+ }
42
+ getTestActorData(_name) {
43
+ return {
44
+ type: "character",
45
+ system: {
46
+ attributes: {
47
+ hp: { value: 10, max: 10 },
48
+ },
49
+ },
50
+ };
51
+ }
52
+ getCurrencyVerifyParams(actorName, amount, currency) {
53
+ return {
54
+ key: "pf2e-currency-added",
55
+ predicate: (data, extra) => {
56
+ return (data.actorName === extra.actorName &&
57
+ data.currency === extra.currency &&
58
+ data.amount === extra.amount);
59
+ },
60
+ };
61
+ }
62
+ }
@@ -0,0 +1,18 @@
1
+ import { Page } from "@playwright/test";
2
+ import { DeprecationTracker } from "../deprecations.js";
3
+ /**
4
+ * Extended Playwright Page with Foundry-specific properties.
5
+ */
6
+ export interface FoundryPage extends Page {
7
+ deprecationTracker?: DeprecationTracker;
8
+ }
9
+ /**
10
+ * Foundry VTT User Roles as defined in CONST.USER_ROLES
11
+ */
12
+ export declare enum UserRole {
13
+ NONE = 0,
14
+ PLAYER = 1,
15
+ TRUSTED = 2,
16
+ ASSISTANT = 3,
17
+ GAMEMASTER = 4
18
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Foundry VTT User Roles as defined in CONST.USER_ROLES
3
+ */
4
+ export var UserRole;
5
+ (function (UserRole) {
6
+ UserRole[UserRole["NONE"] = 0] = "NONE";
7
+ UserRole[UserRole["PLAYER"] = 1] = "PLAYER";
8
+ UserRole[UserRole["TRUSTED"] = 2] = "TRUSTED";
9
+ UserRole[UserRole["ASSISTANT"] = 3] = "ASSISTANT";
10
+ UserRole[UserRole["GAMEMASTER"] = 4] = "GAMEMASTER";
11
+ })(UserRole || (UserRole = {}));
@@ -0,0 +1,35 @@
1
+ import { Page } from "@playwright/test";
2
+ /**
3
+ * Interface for UI-specific logic and selectors in Foundry VTT.
4
+ */
5
+ export interface UIAdapter {
6
+ /** The unique ID of the module or system this adapter is for. */
7
+ id: string;
8
+ /**
9
+ * Returns a selector for an actor sheet.
10
+ */
11
+ getActorSheetSelector(): string;
12
+ /**
13
+ * Switches to a specific tab on an application (e.g., Actor sheet).
14
+ * @param page The Playwright Page object.
15
+ * @param appSelector The selector for the application window.
16
+ * @param tabName The name of the tab to switch to.
17
+ */
18
+ switchAppTab(page: Page, appSelector: string, tabName: string): Promise<void>;
19
+ /**
20
+ * Expands a collapsible section if it is currently collapsed.
21
+ * @param page The Playwright Page object.
22
+ * @param appSelector The selector for the application window.
23
+ * @param sectionName The name/label of the section.
24
+ */
25
+ handleCollapsibleSection(page: Page, appSelector: string, sectionName: string): Promise<void>;
26
+ }
27
+ /**
28
+ * Default UI adapter for standard Foundry VTT interface.
29
+ */
30
+ export declare class DefaultUIAdapter implements UIAdapter {
31
+ id: string;
32
+ getActorSheetSelector(): string;
33
+ switchAppTab(page: Page, appSelector: string, tabName: string): Promise<void>;
34
+ handleCollapsibleSection(page: Page, appSelector: string, sectionName: string): Promise<void>;
35
+ }