@web/test-runner-commands 0.6.2 → 0.6.5

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.
@@ -69,6 +69,10 @@ export function sendKeys(options) {
69
69
  return executeServerCommand('send-keys', options);
70
70
  }
71
71
 
72
+ export function selectOption(options) {
73
+ return executeServerCommand('select-option', options);
74
+ }
75
+
72
76
  export function sendMouse(options) {
73
77
  return executeServerCommand('send-mouse', options);
74
78
  }
@@ -211,6 +215,8 @@ export async function compareSnapshot({ name, content }) {
211
215
  `New:\n${content}`,
212
216
  );
213
217
  }
218
+ } else if (currentSnapshot === content) {
219
+ return;
214
220
  }
215
221
  }
216
222
  await saveSnapshot({ name, content });
@@ -0,0 +1,17 @@
1
+ import { TestRunnerPlugin } from '@web/test-runner-core';
2
+ declare type SelectByValuePayload = {
3
+ selector: string;
4
+ value: string;
5
+ };
6
+ declare type SelectByLabelPayload = {
7
+ selector: string;
8
+ label: string;
9
+ };
10
+ declare type SelectMultipleValuesPayload = {
11
+ selector: string;
12
+ values: string[];
13
+ };
14
+ export declare type SelectOptionPayload = SelectByLabelPayload | SelectByValuePayload | SelectMultipleValuesPayload;
15
+ export declare function selectOptionPlugin(): TestRunnerPlugin<SelectOptionPayload>;
16
+ export {};
17
+ //# sourceMappingURL=selectOptionPlugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptionPlugin.d.ts","sourceRoot":"","sources":["../src/selectOptionPlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD,aAAK,oBAAoB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,aAAK,oBAAoB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAChE,aAAK,2BAA2B,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAE1E,oBAAY,mBAAmB,GAC3B,oBAAoB,GACpB,oBAAoB,GACpB,2BAA2B,CAAC;AA8ChC,wBAAgB,kBAAkB,IAAI,gBAAgB,CAAC,mBAAmB,CAAC,CAgE1E"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectOptionPlugin = void 0;
4
+ function isObject(payload) {
5
+ return payload != null && typeof payload === 'object';
6
+ }
7
+ function isSelectOptionPayload(payload) {
8
+ const validOptions = ['value', 'label', 'values'];
9
+ if (!isObject(payload))
10
+ throw new Error('You must provide a `SelectOptionPayload` object');
11
+ if (!payload.selector || typeof payload.selector !== 'string') {
12
+ throw new Error(`You must provide a selector representing the select you wish to locate`);
13
+ }
14
+ const numberOfValidOptions = Object.keys(payload).filter(key => validOptions.includes(key)).length;
15
+ const unknownOptions = Object.keys(payload).filter(key => ![...validOptions, 'selector'].includes(key));
16
+ if (numberOfValidOptions === 0)
17
+ throw new Error(`You must provide one of the following properties to pass to the browser runner: ${validOptions.join(', ')}.`);
18
+ if (unknownOptions.length > 0) {
19
+ throw new Error('Unknown options `' + unknownOptions.join(', ') + '` present.');
20
+ }
21
+ return true;
22
+ }
23
+ function isValuePayload(payload) {
24
+ return 'selector' in payload && 'value' in payload;
25
+ }
26
+ function isLabelPayload(payload) {
27
+ return 'selector' in payload && 'label' in payload;
28
+ }
29
+ function isMultiplePayload(payload) {
30
+ return 'selector' in payload && 'values' in payload;
31
+ }
32
+ function selectOptionPlugin() {
33
+ return {
34
+ name: 'select-option-command',
35
+ async executeCommand({ command, payload, session }) {
36
+ if (command === 'select-option') {
37
+ if (!isSelectOptionPayload(payload) || !payload) {
38
+ throw new Error('You must provide a `SelectOptionPayload` object');
39
+ }
40
+ // handle specific behavior for playwright
41
+ if (session.browser.type === 'playwright') {
42
+ const page = session.browser.getPage(session.id);
43
+ if (isValuePayload(payload)) {
44
+ const { selector, value } = payload;
45
+ await page.locator(selector).selectOption(value);
46
+ return true;
47
+ }
48
+ else if (isLabelPayload(payload)) {
49
+ const { selector, label } = payload;
50
+ await page.locator(selector).selectOption({ label });
51
+ return true;
52
+ }
53
+ else if (isMultiplePayload(payload)) {
54
+ const { selector, values } = payload;
55
+ await page.locator(selector).selectOption([...values]);
56
+ return true;
57
+ }
58
+ }
59
+ // handle specific behavior for puppeteer
60
+ if (session.browser.type === 'puppeteer') {
61
+ const page = session.browser.getPage(session.id);
62
+ if (isValuePayload(payload)) {
63
+ const { selector, value } = payload;
64
+ await page.select(selector, value);
65
+ return true;
66
+ }
67
+ else if (isMultiplePayload(payload)) {
68
+ const { selector, values } = payload;
69
+ await page.select(selector, ...values);
70
+ return true;
71
+ }
72
+ else {
73
+ throw new Error(`Puppeteer only supports selection of an option by its value(s):
74
+ https://pptr.dev/next/api/puppeteer.page.select#parameters
75
+ `);
76
+ }
77
+ }
78
+ // handle specific behavior for webdriver
79
+ if (session.browser.type === 'webdriver') {
80
+ throw new Error(`Selecting an option via a browser driver command is not currently implemented in WebDriver yet.
81
+ https://www.selenium.dev/documentation/webdriver/elements/select_lists/
82
+
83
+ When using WebDriver, the current recommended (September 2022) approach is to:
84
+ - locate your select element (via querySelector or querySelectorAll)
85
+ - focus the element
86
+ - use sendKeys() to type the label of the option you wish to select
87
+ - use select.dispatchEvent(new Event('change')) to trigger a change event on the select.
88
+ `);
89
+ }
90
+ // you might not be able to support all browser launchers
91
+ throw new Error(`Selection of select element options is not supported for browser type ${session.browser.type}.`);
92
+ }
93
+ },
94
+ };
95
+ }
96
+ exports.selectOptionPlugin = selectOptionPlugin;
97
+ //# sourceMappingURL=selectOptionPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptionPlugin.js","sourceRoot":"","sources":["../src/selectOptionPlugin.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshotPlugin.d.ts","sourceRoot":"","sources":["../src/snapshotPlugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAwBzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAiGD,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,oBAAoB,GAAG,gBAAgB,CA2B9E"}
1
+ {"version":3,"file":"snapshotPlugin.d.ts","sourceRoot":"","sources":["../src/snapshotPlugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAyBzD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAuHD,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,oBAAoB,GAAG,gBAAgB,CAoD9E"}
@@ -43,6 +43,7 @@ function getSnapshotPath(testFile) {
43
43
  class SnapshotStore {
44
44
  constructor() {
45
45
  this.snapshots = new Map();
46
+ this.sessionToSnapshotPath = new Map();
46
47
  this.readOperations = new Map();
47
48
  }
48
49
  async get(testFilePath) {
@@ -52,9 +53,10 @@ class SnapshotStore {
52
53
  // something else is already reading, wait for it
53
54
  await ((_a = this.readOperations.get(snapshotPath)) === null || _a === void 0 ? void 0 : _a.promise);
54
55
  }
55
- if (this.snapshots.has(testFilePath)) {
56
+ const cachedContent = this.snapshots.get(snapshotPath);
57
+ if (cachedContent) {
56
58
  // return from cache
57
- return this.snapshots.get(testFilePath);
59
+ return cachedContent;
58
60
  }
59
61
  const promiseObj = { resolve: () => { }, promise: Promise.resolve() };
60
62
  promiseObj.promise = new Promise(resolve => {
@@ -71,7 +73,7 @@ class SnapshotStore {
71
73
  this.readOperations.delete(snapshotPath);
72
74
  return content;
73
75
  }
74
- async saveSnapshot(testFilePath, name, updatedSnapshot) {
76
+ async saveSnapshot(sessionId, testFilePath, name, updatedSnapshot) {
75
77
  const snapshotPath = getSnapshotPath(testFilePath);
76
78
  const nameStr = JSON.stringify(name);
77
79
  const startMarker = `snapshots[${nameStr}]`;
@@ -79,7 +81,7 @@ class SnapshotStore {
79
81
  const replacement = updatedSnapshot
80
82
  ? `${startMarker} = \n\`${updatedSnapshot}\`;\n${endMarker}`
81
83
  : '';
82
- const content = await this.get(snapshotPath);
84
+ const content = await this.get(testFilePath);
83
85
  let updatedContent;
84
86
  const startIndex = content.indexOf(startMarker);
85
87
  if (startIndex !== -1) {
@@ -96,7 +98,21 @@ class SnapshotStore {
96
98
  // add new snapshot
97
99
  updatedContent = `${content}${replacement}`;
98
100
  }
101
+ if (updatedContent === content) {
102
+ // snapshot did not actually change, avoid marking snapshot as dirty
103
+ return;
104
+ }
105
+ this.sessionToSnapshotPath.set(sessionId, snapshotPath);
99
106
  this.snapshots.set(snapshotPath, updatedContent);
107
+ }
108
+ getSnapshotPathForSession(sessionId) {
109
+ return this.sessionToSnapshotPath.get(sessionId);
110
+ }
111
+ async writeSnapshot(snapshotPath) {
112
+ const updatedContent = this.snapshots.get(snapshotPath);
113
+ if (!updatedContent) {
114
+ throw new Error('Unexpected error while writing snapshots, could not find snapshot content.');
115
+ }
100
116
  if (updatedContent.includes('/* end snapshot')) {
101
117
  // update or create snapshot
102
118
  const fileDir = path_1.default.dirname(snapshotPath);
@@ -114,8 +130,31 @@ class SnapshotStore {
114
130
  function snapshotPlugin(config) {
115
131
  const updateSnapshots = config && config.updateSnapshots;
116
132
  const snapshots = new SnapshotStore();
133
+ const writePromises = new Set();
117
134
  return {
118
135
  name: 'file-commands',
136
+ serverStart({ webSockets }) {
137
+ webSockets.on('message', async ({ data }) => {
138
+ const { type, sessionId } = data;
139
+ if (type === 'wtr-session-finished') {
140
+ if (typeof sessionId !== 'string') {
141
+ throw new Error('Missing session id in wtr-session-finished event');
142
+ }
143
+ const snapshotPath = snapshots.getSnapshotPathForSession(sessionId);
144
+ if (!snapshotPath) {
145
+ return;
146
+ }
147
+ const writePromise = snapshots.writeSnapshot(snapshotPath);
148
+ writePromises.add(writePromise);
149
+ await writePromise;
150
+ writePromises.delete(writePromise);
151
+ }
152
+ });
153
+ },
154
+ async serverStop() {
155
+ // ensure all write operations are finished
156
+ await Promise.all([...writePromises]);
157
+ },
119
158
  async executeCommand({ command, payload, session }) {
120
159
  if (command === 'get-snapshot-config') {
121
160
  return { updateSnapshots };
@@ -128,7 +167,7 @@ function snapshotPlugin(config) {
128
167
  if (!isSaveSnapshotPayload(payload)) {
129
168
  throw new Error('Invalid save snapshot payload');
130
169
  }
131
- await snapshots.saveSnapshot(session.testFile, payload.name, payload.content);
170
+ await snapshots.saveSnapshot(session.id, session.testFile, payload.name, payload.content);
132
171
  return true;
133
172
  }
134
173
  },
@@ -1 +1 @@
1
- {"version":3,"file":"snapshotPlugin.js","sourceRoot":"","sources":["../src/snapshotPlugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshotPlugin.js","sourceRoot":"","sources":["../src/snapshotPlugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web/test-runner-commands",
3
- "version": "0.6.2",
3
+ "version": "0.6.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -48,13 +48,13 @@
48
48
  "commands"
49
49
  ],
50
50
  "dependencies": {
51
- "@web/test-runner-core": "^0.10.20",
51
+ "@web/test-runner-core": "^0.10.27",
52
52
  "mkdirp": "^1.0.4"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@web/test-runner-chrome": "^0.10.2",
56
56
  "@web/test-runner-playwright": "^0.8.9",
57
- "@web/test-runner-webdriver": "^0.5.0",
57
+ "@web/test-runner-webdriver": "^0.5.1",
58
58
  "mocha": "^8.2.1"
59
59
  }
60
60
  }
@@ -0,0 +1,122 @@
1
+ import { TestRunnerPlugin } from '@web/test-runner-core';
2
+ import type { ChromeLauncher } from '@web/test-runner-chrome';
3
+ import type { PlaywrightLauncher } from '@web/test-runner-playwright';
4
+
5
+ type SelectByValuePayload = { selector: string; value: string };
6
+ type SelectByLabelPayload = { selector: string; label: string };
7
+ type SelectMultipleValuesPayload = { selector: string; values: string[] };
8
+
9
+ export type SelectOptionPayload =
10
+ | SelectByLabelPayload
11
+ | SelectByValuePayload
12
+ | SelectMultipleValuesPayload;
13
+
14
+ function isObject(payload: unknown): payload is Record<string, unknown> {
15
+ return payload != null && typeof payload === 'object';
16
+ }
17
+
18
+ function isSelectOptionPayload(payload: unknown): boolean {
19
+ const validOptions = ['value', 'label', 'values'];
20
+
21
+ if (!isObject(payload)) throw new Error('You must provide a `SelectOptionPayload` object');
22
+
23
+ if (!payload.selector || typeof payload.selector !== 'string') {
24
+ throw new Error(`You must provide a selector representing the select you wish to locate`);
25
+ }
26
+
27
+ const numberOfValidOptions = Object.keys(payload).filter(key =>
28
+ validOptions.includes(key),
29
+ ).length;
30
+ const unknownOptions = Object.keys(payload).filter(
31
+ key => ![...validOptions, 'selector'].includes(key),
32
+ );
33
+
34
+ if (numberOfValidOptions === 0)
35
+ throw new Error(
36
+ `You must provide one of the following properties to pass to the browser runner: ${validOptions.join(
37
+ ', ',
38
+ )}.`,
39
+ );
40
+ if (unknownOptions.length > 0) {
41
+ throw new Error('Unknown options `' + unknownOptions.join(', ') + '` present.');
42
+ }
43
+ return true;
44
+ }
45
+
46
+ function isValuePayload(payload: SelectOptionPayload): payload is SelectByValuePayload {
47
+ return 'selector' in payload && 'value' in payload;
48
+ }
49
+
50
+ function isLabelPayload(payload: SelectOptionPayload): payload is SelectByLabelPayload {
51
+ return 'selector' in payload && 'label' in payload;
52
+ }
53
+
54
+ function isMultiplePayload(payload: SelectOptionPayload): payload is SelectMultipleValuesPayload {
55
+ return 'selector' in payload && 'values' in payload;
56
+ }
57
+
58
+ export function selectOptionPlugin(): TestRunnerPlugin<SelectOptionPayload> {
59
+ return {
60
+ name: 'select-option-command',
61
+ async executeCommand({ command, payload, session }): Promise<any> {
62
+ if (command === 'select-option') {
63
+ if (!isSelectOptionPayload(payload) || !payload) {
64
+ throw new Error('You must provide a `SelectOptionPayload` object');
65
+ }
66
+ // handle specific behavior for playwright
67
+ if (session.browser.type === 'playwright') {
68
+ const page = (session.browser as PlaywrightLauncher).getPage(session.id);
69
+ if (isValuePayload(payload)) {
70
+ const { selector, value } = payload;
71
+ await page.locator(selector).selectOption(value);
72
+ return true;
73
+ } else if (isLabelPayload(payload)) {
74
+ const { selector, label } = payload;
75
+ await page.locator(selector).selectOption({ label });
76
+ return true;
77
+ } else if (isMultiplePayload(payload)) {
78
+ const { selector, values } = payload;
79
+ await page.locator(selector).selectOption([...values]);
80
+ return true;
81
+ }
82
+ }
83
+
84
+ // handle specific behavior for puppeteer
85
+ if (session.browser.type === 'puppeteer') {
86
+ const page = (session.browser as ChromeLauncher).getPage(session.id);
87
+ if (isValuePayload(payload)) {
88
+ const { selector, value } = payload;
89
+ await page.select(selector, value);
90
+ return true;
91
+ } else if (isMultiplePayload(payload)) {
92
+ const { selector, values } = payload;
93
+ await page.select(selector, ...values);
94
+ return true;
95
+ } else {
96
+ throw new Error(`Puppeteer only supports selection of an option by its value(s):
97
+ https://pptr.dev/next/api/puppeteer.page.select#parameters
98
+ `);
99
+ }
100
+ }
101
+
102
+ // handle specific behavior for webdriver
103
+ if (session.browser.type === 'webdriver') {
104
+ throw new Error(`Selecting an option via a browser driver command is not currently implemented in WebDriver yet.
105
+ https://www.selenium.dev/documentation/webdriver/elements/select_lists/
106
+
107
+ When using WebDriver, the current recommended (September 2022) approach is to:
108
+ - locate your select element (via querySelector or querySelectorAll)
109
+ - focus the element
110
+ - use sendKeys() to type the label of the option you wish to select
111
+ - use select.dispatchEvent(new Event('change')) to trigger a change event on the select.
112
+ `);
113
+ }
114
+
115
+ // you might not be able to support all browser launchers
116
+ throw new Error(
117
+ `Selection of select element options is not supported for browser type ${session.browser.type}.`,
118
+ );
119
+ }
120
+ },
121
+ };
122
+ }
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-empty-function */
2
2
  import { TestRunnerPlugin } from '@web/test-runner-core';
3
+ import { ServerStartParams } from '@web/dev-server-core';
3
4
  import path from 'path';
4
5
  import fs from 'fs';
5
6
  import { promisify } from 'util';
@@ -46,6 +47,7 @@ function getSnapshotPath(testFile: string) {
46
47
 
47
48
  class SnapshotStore {
48
49
  private snapshots = new Map<string, string>();
50
+ private sessionToSnapshotPath = new Map<string, string>();
49
51
  private readOperations = new Map<string, { promise: Promise<void>; resolve: () => void }>();
50
52
 
51
53
  async get(testFilePath: string): Promise<string> {
@@ -56,9 +58,10 @@ class SnapshotStore {
56
58
  await this.readOperations.get(snapshotPath)?.promise;
57
59
  }
58
60
 
59
- if (this.snapshots.has(testFilePath)) {
61
+ const cachedContent = this.snapshots.get(snapshotPath);
62
+ if (cachedContent) {
60
63
  // return from cache
61
- return this.snapshots.get(testFilePath)!;
64
+ return cachedContent;
62
65
  }
63
66
 
64
67
  const promiseObj = { resolve: () => {}, promise: Promise.resolve() };
@@ -66,7 +69,6 @@ class SnapshotStore {
66
69
  promiseObj.resolve = resolve;
67
70
  });
68
71
  this.readOperations.set(testFilePath, promiseObj);
69
-
70
72
  // store in cache
71
73
  const content = (await fileExists(snapshotPath))
72
74
  ? await readFile(snapshotPath, 'utf-8')
@@ -79,7 +81,12 @@ class SnapshotStore {
79
81
  return content;
80
82
  }
81
83
 
82
- async saveSnapshot(testFilePath: string, name: string, updatedSnapshot: string) {
84
+ async saveSnapshot(
85
+ sessionId: string,
86
+ testFilePath: string,
87
+ name: string,
88
+ updatedSnapshot: string,
89
+ ) {
83
90
  const snapshotPath = getSnapshotPath(testFilePath);
84
91
  const nameStr = JSON.stringify(name);
85
92
  const startMarker = `snapshots[${nameStr}]`;
@@ -88,7 +95,7 @@ class SnapshotStore {
88
95
  ? `${startMarker} = \n\`${updatedSnapshot}\`;\n${endMarker}`
89
96
  : '';
90
97
 
91
- const content = await this.get(snapshotPath);
98
+ const content = await this.get(testFilePath);
92
99
  let updatedContent: string;
93
100
 
94
101
  const startIndex = content.indexOf(startMarker);
@@ -107,8 +114,24 @@ class SnapshotStore {
107
114
  // add new snapshot
108
115
  updatedContent = `${content}${replacement}`;
109
116
  }
117
+ if (updatedContent === content) {
118
+ // snapshot did not actually change, avoid marking snapshot as dirty
119
+ return;
120
+ }
110
121
 
122
+ this.sessionToSnapshotPath.set(sessionId, snapshotPath);
111
123
  this.snapshots.set(snapshotPath, updatedContent);
124
+ }
125
+
126
+ getSnapshotPathForSession(sessionId: string) {
127
+ return this.sessionToSnapshotPath.get(sessionId);
128
+ }
129
+
130
+ async writeSnapshot(snapshotPath: string) {
131
+ const updatedContent = this.snapshots.get(snapshotPath);
132
+ if (!updatedContent) {
133
+ throw new Error('Unexpected error while writing snapshots, could not find snapshot content.');
134
+ }
112
135
  if (updatedContent.includes('/* end snapshot')) {
113
136
  // update or create snapshot
114
137
  const fileDir = path.dirname(snapshotPath);
@@ -130,10 +153,35 @@ export interface SnapshotPluginConfig {
130
153
  export function snapshotPlugin(config?: SnapshotPluginConfig): TestRunnerPlugin {
131
154
  const updateSnapshots = config && config.updateSnapshots;
132
155
  const snapshots = new SnapshotStore();
156
+ const writePromises = new Set<Promise<void>>();
133
157
 
134
158
  return {
135
159
  name: 'file-commands',
136
160
 
161
+ serverStart({ webSockets }: ServerStartParams) {
162
+ webSockets!.on('message', async ({ data }) => {
163
+ const { type, sessionId } = data;
164
+ if (type === 'wtr-session-finished') {
165
+ if (typeof sessionId !== 'string') {
166
+ throw new Error('Missing session id in wtr-session-finished event');
167
+ }
168
+ const snapshotPath = snapshots.getSnapshotPathForSession(sessionId);
169
+ if (!snapshotPath) {
170
+ return;
171
+ }
172
+ const writePromise = snapshots.writeSnapshot(snapshotPath);
173
+ writePromises.add(writePromise);
174
+ await writePromise;
175
+ writePromises.delete(writePromise);
176
+ }
177
+ });
178
+ },
179
+
180
+ async serverStop() {
181
+ // ensure all write operations are finished
182
+ await Promise.all([...writePromises]);
183
+ },
184
+
137
185
  async executeCommand({ command, payload, session }) {
138
186
  if (command === 'get-snapshot-config') {
139
187
  return { updateSnapshots };
@@ -149,7 +197,7 @@ export function snapshotPlugin(config?: SnapshotPluginConfig): TestRunnerPlugin
149
197
  throw new Error('Invalid save snapshot payload');
150
198
  }
151
199
 
152
- await snapshots.saveSnapshot(session.testFile, payload.name, payload.content);
200
+ await snapshots.saveSnapshot(session.id, session.testFile, payload.name, payload.content);
153
201
  return true;
154
202
  }
155
203
  },