@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/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
|
+
}
|