creevey 0.10.0-beta.43 → 0.10.0-beta.44
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/CHANGELOG.md +282 -0
- package/dist/client/addon/controller.js +1 -1
- package/dist/client/addon/controller.js.map +1 -1
- package/dist/client/addon/withCreevey.js +1 -18
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/PageHeader/PageHeader.js +13 -4
- package/dist/client/shared/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/client/shared/creeveyClientApi.js +10 -0
- package/dist/client/shared/creeveyClientApi.js.map +1 -1
- package/dist/client/web/CreeveyApp.d.ts +1 -0
- package/dist/client/web/CreeveyApp.js +1 -0
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/client/web/CreeveyContext.d.ts +1 -0
- package/dist/client/web/CreeveyContext.js +1 -0
- package/dist/client/web/CreeveyContext.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +9 -8
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +13 -3
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +2 -3
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.js +2 -3
- package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js +1 -0
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js.map +1 -1
- package/dist/client/web/assets/{index-C47njyZV.js → index-BU4jjKVC.js} +68 -68
- package/dist/client/web/index.html +1 -1
- package/dist/client/web/index.js +8 -3
- package/dist/client/web/index.js.map +1 -1
- package/dist/creevey.d.ts +1 -1
- package/dist/creevey.js +1 -22
- package/dist/creevey.js.map +1 -1
- package/dist/playwright-reporter.d.ts +2 -0
- package/dist/playwright-reporter.js +5 -0
- package/dist/playwright-reporter.js.map +1 -0
- package/dist/playwright.d.ts +1 -1
- package/dist/server/config.js +8 -1
- package/dist/server/config.js.map +1 -1
- package/dist/server/index.js +10 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/master/api.d.ts +15 -5
- package/dist/server/master/api.js +89 -27
- package/dist/server/master/api.js.map +1 -1
- package/dist/server/master/handlers/capture-handler.d.ts +2 -0
- package/dist/server/master/handlers/capture-handler.js +35 -0
- package/dist/server/master/handlers/capture-handler.js.map +1 -0
- package/dist/server/master/handlers/index.d.ts +4 -0
- package/dist/server/master/handlers/index.js +21 -0
- package/dist/server/master/handlers/index.js.map +1 -0
- package/dist/server/master/handlers/ping-handler.d.ts +2 -0
- package/dist/server/master/handlers/ping-handler.js +7 -0
- package/dist/server/master/handlers/ping-handler.js.map +1 -0
- package/dist/server/master/handlers/static-handler.d.ts +2 -0
- package/dist/server/master/handlers/static-handler.js +32 -0
- package/dist/server/master/handlers/static-handler.js.map +1 -0
- package/dist/server/master/handlers/stories-handler.d.ts +2 -0
- package/dist/server/master/handlers/stories-handler.js +38 -0
- package/dist/server/master/handlers/stories-handler.js.map +1 -0
- package/dist/server/master/master.js +7 -24
- package/dist/server/master/master.js.map +1 -1
- package/dist/server/master/runner.d.ts +4 -6
- package/dist/server/master/runner.js +30 -127
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/master/server.js +77 -87
- package/dist/server/master/server.js.map +1 -1
- package/dist/server/master/start.d.ts +1 -2
- package/dist/server/master/start.js +11 -29
- package/dist/server/master/start.js.map +1 -1
- package/dist/server/master/testsManager.d.ts +81 -0
- package/dist/server/master/testsManager.js +281 -0
- package/dist/server/master/testsManager.js.map +1 -0
- package/dist/server/playwright/reporter.d.ts +87 -0
- package/dist/server/playwright/reporter.js +351 -0
- package/dist/server/playwright/reporter.js.map +1 -0
- package/dist/server/selenium/internal.js +20 -2
- package/dist/server/selenium/internal.js.map +1 -1
- package/dist/server/selenium/selenoid.js +4 -0
- package/dist/server/selenium/selenoid.js.map +1 -1
- package/dist/server/shutdown.d.ts +1 -0
- package/dist/server/shutdown.js +23 -0
- package/dist/server/shutdown.js.map +1 -0
- package/dist/server/stories.d.ts +0 -1
- package/dist/server/stories.js +0 -12
- package/dist/server/stories.js.map +1 -1
- package/dist/server/ui-update.d.ts +10 -0
- package/dist/server/ui-update.js +39 -0
- package/dist/server/ui-update.js.map +1 -0
- package/dist/server/utils.d.ts +6 -0
- package/dist/server/utils.js +39 -8
- package/dist/server/utils.js.map +1 -1
- package/dist/server/worker/start.js +1 -1
- package/dist/server/worker/start.js.map +1 -1
- package/dist/types.d.ts +14 -8
- package/dist/types.js.map +1 -1
- package/docs/examples/playwright-reporter-example.ts +202 -0
- package/docs/migration-0.9-to-0.10.md +144 -0
- package/docs/playwright-reporter.md +357 -0
- package/package.json +9 -13
- package/src/client/addon/controller.ts +1 -1
- package/src/client/addon/withCreevey.ts +2 -16
- package/src/client/shared/components/PageHeader/PageHeader.tsx +18 -4
- package/src/client/shared/creeveyClientApi.ts +10 -0
- package/src/client/web/CreeveyApp.tsx +2 -0
- package/src/client/web/CreeveyContext.tsx +2 -0
- package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +19 -17
- package/src/client/web/CreeveyView/SideBar/SideBarHeader.tsx +18 -3
- package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +9 -7
- package/src/client/web/CreeveyView/SideBar/TestLink.tsx +8 -6
- package/src/client/web/CreeveyView/SideBar/TestsStatus.tsx +1 -0
- package/src/client/web/index.tsx +8 -3
- package/src/creevey.ts +1 -24
- package/src/playwright-reporter.ts +3 -0
- package/src/server/config.ts +9 -1
- package/src/server/index.ts +11 -4
- package/src/server/master/api.ts +95 -26
- package/src/server/master/handlers/capture-handler.ts +39 -0
- package/src/server/master/handlers/index.ts +4 -0
- package/src/server/master/handlers/ping-handler.ts +5 -0
- package/src/server/master/handlers/static-handler.ts +29 -0
- package/src/server/master/handlers/stories-handler.ts +48 -0
- package/src/server/master/master.ts +10 -27
- package/src/server/master/runner.ts +38 -132
- package/src/server/master/server.ts +93 -97
- package/src/server/master/start.ts +17 -41
- package/src/server/master/testsManager.ts +315 -0
- package/src/server/playwright/reporter.ts +386 -0
- package/src/server/selenium/internal.ts +23 -3
- package/src/server/selenium/selenoid.ts +5 -0
- package/src/server/shutdown.ts +19 -0
- package/src/server/stories.ts +1 -12
- package/src/server/ui-update.ts +46 -0
- package/src/server/utils.ts +40 -9
- package/src/server/worker/start.ts +1 -1
- package/src/types.ts +14 -8
@@ -373,12 +373,32 @@ export class InternalBrowser {
|
|
373
373
|
}
|
374
374
|
|
375
375
|
async loadStoriesFromBrowser(): Promise<StoriesRaw> {
|
376
|
-
const
|
377
|
-
|
376
|
+
const result = await this.#browser.executeAsyncScript<
|
377
|
+
[error?: { message: string; stack?: string } | null, stories?: StoriesRaw]
|
378
|
+
>(function (
|
379
|
+
callback: (response: [error?: { message: string; stack?: string } | null, stories?: StoriesRaw]) => void,
|
378
380
|
) {
|
379
|
-
|
381
|
+
window
|
382
|
+
.__CREEVEY_GET_STORIES__()
|
383
|
+
.then((stories) => {
|
384
|
+
callback([null, stories]);
|
385
|
+
})
|
386
|
+
.catch((error: unknown) => {
|
387
|
+
const errorInfo = {
|
388
|
+
message: error instanceof Error ? error.message : String(error),
|
389
|
+
stack: error instanceof Error ? error.stack : undefined,
|
390
|
+
};
|
391
|
+
callback([errorInfo]);
|
392
|
+
});
|
380
393
|
});
|
381
394
|
|
395
|
+
const [error, stories] = result;
|
396
|
+
|
397
|
+
if (error) {
|
398
|
+
const errorObj = new Error(error.message);
|
399
|
+
if (error.stack) errorObj.stack = error.stack;
|
400
|
+
throw errorObj;
|
401
|
+
}
|
382
402
|
if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
|
383
403
|
|
384
404
|
return stories;
|
@@ -7,6 +7,7 @@ import { Config, BrowserConfigObject } from '../../types.js';
|
|
7
7
|
import { downloadBinary, getCreeveyCache, killTree } from '../utils.js';
|
8
8
|
import { pullImages, runImage } from '../docker.js';
|
9
9
|
import { subscribeOn } from '../messages.js';
|
10
|
+
import { removeWorkerContainer } from '../worker/context.js';
|
10
11
|
|
11
12
|
async function createSelenoidConfig(
|
12
13
|
browsers: BrowserConfigObject[],
|
@@ -147,5 +148,9 @@ export async function startSelenoidContainer(config: Config, debug: boolean): Pr
|
|
147
148
|
},
|
148
149
|
};
|
149
150
|
|
151
|
+
subscribeOn('shutdown', () => {
|
152
|
+
void removeWorkerContainer();
|
153
|
+
});
|
154
|
+
|
150
155
|
return runImage(selenoidImage, ['-limit', String(limit)], selenoidOptions, debug);
|
151
156
|
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import cluster from 'cluster';
|
2
|
+
import { subscribeOn } from './messages.js';
|
3
|
+
import { shutdownOnException, isShuttingDown } from './utils.js';
|
4
|
+
|
5
|
+
if (cluster.isWorker) {
|
6
|
+
subscribeOn('shutdown', () => {
|
7
|
+
isShuttingDown.current = true;
|
8
|
+
});
|
9
|
+
}
|
10
|
+
|
11
|
+
process.on('uncaughtException', shutdownOnException);
|
12
|
+
process.on('unhandledRejection', shutdownOnException);
|
13
|
+
// TODO SIGINT Stuck with selenium
|
14
|
+
process.on('SIGINT', () => {
|
15
|
+
if (isShuttingDown.current) {
|
16
|
+
process.exit(-1);
|
17
|
+
}
|
18
|
+
isShuttingDown.current = true;
|
19
|
+
});
|
package/src/server/stories.ts
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
import path from 'path';
|
2
|
-
import { mkdirSync, writeFileSync } from 'fs';
|
3
1
|
import { createHash } from 'crypto';
|
4
2
|
import _ from 'lodash';
|
5
3
|
import type {
|
@@ -12,7 +10,7 @@ import type {
|
|
12
10
|
CreeveyTestFunction,
|
13
11
|
CreeveyTestContext,
|
14
12
|
} from '../types.js';
|
15
|
-
import { isDefined
|
13
|
+
import { isDefined } from '../types.js';
|
16
14
|
import { shouldSkip } from './utils.js';
|
17
15
|
|
18
16
|
function storyTestFabric(delay?: number, testFn?: CreeveyTestFunction) {
|
@@ -129,12 +127,3 @@ export async function loadTestsFromStories(
|
|
129
127
|
|
130
128
|
return tests;
|
131
129
|
}
|
132
|
-
|
133
|
-
export function saveTestsJson(tests: Record<string, unknown>, dstPath: string = process.cwd()): void {
|
134
|
-
mkdirSync(dstPath, { recursive: true });
|
135
|
-
writeFileSync(
|
136
|
-
path.join(dstPath, 'tests.json'),
|
137
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
138
|
-
JSON.stringify(tests, (_, value) => (isFunction(value) ? value.toString() : value), 2),
|
139
|
-
);
|
140
|
-
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import { Config } from '../types.js';
|
2
|
+
import { logger } from './logger.js';
|
3
|
+
import { TestsManager } from './master/testsManager.js';
|
4
|
+
import { start as startServer } from './master/server.js';
|
5
|
+
import { CreeveyApi } from './master/api.js';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* UI Update Mode implementation.
|
9
|
+
* This mode allows users to review and approve screenshots from the browser interface.
|
10
|
+
* It combines the functionality of both --ui and --update flags.
|
11
|
+
*
|
12
|
+
* @param config Creevey configuration
|
13
|
+
* @param port Port to run the server on
|
14
|
+
*/
|
15
|
+
export async function uiUpdate(config: Config, port: number): Promise<void> {
|
16
|
+
logger().info('Starting UI Update Mode');
|
17
|
+
|
18
|
+
// Initialize TestsManager with the configured directories
|
19
|
+
const testsManager = new TestsManager(config.screenDir, config.reportDir);
|
20
|
+
|
21
|
+
// Load tests from the report
|
22
|
+
const testsFromReport = testsManager.loadTestsFromReport();
|
23
|
+
|
24
|
+
if (Object.keys(testsFromReport).length === 0) {
|
25
|
+
logger().warn('No tests found in report. Run tests first to generate report data.');
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
|
29
|
+
// Set tests in the manager
|
30
|
+
testsManager.updateTests(testsFromReport);
|
31
|
+
|
32
|
+
// Start API server with UI enabled
|
33
|
+
const resolveApi = startServer(config.reportDir, port, true);
|
34
|
+
|
35
|
+
// Initialize API
|
36
|
+
const api = new CreeveyApi(testsManager);
|
37
|
+
|
38
|
+
// Resolve the API for the server
|
39
|
+
resolveApi(api);
|
40
|
+
|
41
|
+
// Save test data to make it available for the UI
|
42
|
+
await testsManager.saveTestData();
|
43
|
+
|
44
|
+
logger().info(`UI Update Mode started on http://localhost:${port}/`);
|
45
|
+
logger().info('You can now review and approve screenshots from the browser.');
|
46
|
+
}
|
package/src/server/utils.ts
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
import fs from 'fs';
|
2
|
-
import
|
2
|
+
import path from 'path';
|
3
3
|
import http from 'http';
|
4
|
+
import https from 'https';
|
5
|
+
import assert from 'assert';
|
4
6
|
import cluster from 'cluster';
|
5
|
-
import
|
7
|
+
import pidtree from 'pidtree';
|
6
8
|
import { fileURLToPath, pathToFileURL } from 'url';
|
7
9
|
import { register as esmRegister } from 'tsx/esm/api';
|
8
10
|
import { register as cjsRegister } from 'tsx/cjs/api';
|
9
11
|
import { SkipOptions, SkipOption, isDefined, TestData, noop, ServerTest, Worker } from '../types.js';
|
10
|
-
import { emitShutdownMessage, sendShutdownMessage } from './messages.js';
|
12
|
+
import { emitShutdownMessage, emitWorkerMessage, sendShutdownMessage } from './messages.js';
|
11
13
|
import { LOCALHOST_REGEXP } from './webdriver.js';
|
12
|
-
import
|
13
|
-
import pidtree from 'pidtree';
|
14
|
+
import { logger } from './logger.js';
|
14
15
|
|
15
16
|
const importMetaUrl = pathToFileURL(__filename).href;
|
16
17
|
|
@@ -89,6 +90,18 @@ export function shouldSkipByOption(
|
|
89
90
|
return skipByBrowser && skipByKind && skipByStory && skipByTest && reason;
|
90
91
|
}
|
91
92
|
|
93
|
+
export function shutdownOnException(reason: unknown): void {
|
94
|
+
if (isShuttingDown.current) return;
|
95
|
+
|
96
|
+
const error = reason instanceof Error ? (reason.stack ?? reason.message) : (reason as string);
|
97
|
+
|
98
|
+
logger().error(error);
|
99
|
+
|
100
|
+
process.exitCode = -1;
|
101
|
+
if (cluster.isWorker) emitWorkerMessage({ type: 'error', payload: { subtype: 'unknown', error } });
|
102
|
+
if (cluster.isPrimary) void shutdownWorkers();
|
103
|
+
}
|
104
|
+
|
92
105
|
export async function shutdownWorkers(): Promise<void> {
|
93
106
|
isShuttingDown.current = true;
|
94
107
|
await Promise.all(
|
@@ -151,7 +164,7 @@ export function resolvePlaywrightBrowserType(browserName: string): (typeof brows
|
|
151
164
|
|
152
165
|
export async function getCreeveyCache(): Promise<string | undefined> {
|
153
166
|
const { default: findCacheDir } = await import('find-cache-dir');
|
154
|
-
return findCacheDir({ name: 'creevey', cwd: dirname(fileURLToPath(importMetaUrl)) });
|
167
|
+
return findCacheDir({ name: 'creevey', cwd: path.dirname(fileURLToPath(importMetaUrl)) });
|
155
168
|
}
|
156
169
|
|
157
170
|
export async function runSequence(seq: (() => unknown)[], predicate: () => boolean): Promise<boolean> {
|
@@ -242,8 +255,8 @@ const [nodeVersion] = process.versions.node.split('.').map(Number);
|
|
242
255
|
export async function loadThroughTSX<T>(
|
243
256
|
callback: (load: (modulePath: string) => Promise<T>) => Promise<T>,
|
244
257
|
): Promise<T> {
|
245
|
-
|
246
|
-
const
|
258
|
+
const unregisterESM = nodeVersion > 18 ? esmRegister() : noop;
|
259
|
+
const unregisterCJS = cjsRegister();
|
247
260
|
|
248
261
|
const result = await callback((modulePath) =>
|
249
262
|
nodeVersion > 18
|
@@ -254,7 +267,9 @@ export async function loadThroughTSX<T>(
|
|
254
267
|
|
255
268
|
// NOTE: `unregister` type is `(() => Promise<void>) | (() => void)`
|
256
269
|
// eslint-disable-next-line @typescript-eslint/await-thenable, @typescript-eslint/no-confusing-void-expression
|
257
|
-
await
|
270
|
+
await unregisterCJS();
|
271
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable, @typescript-eslint/no-confusing-void-expression
|
272
|
+
await unregisterESM();
|
258
273
|
|
259
274
|
return result;
|
260
275
|
}
|
@@ -292,3 +307,19 @@ export function waitOnUrl(waitUrl: string, timeout: number, delay: number) {
|
|
292
307
|
),
|
293
308
|
);
|
294
309
|
}
|
310
|
+
|
311
|
+
/**
|
312
|
+
* Copies static assets to the report directory
|
313
|
+
* @param reportDir Directory where the report will be generated
|
314
|
+
*/
|
315
|
+
export async function copyStatics(reportDir: string): Promise<void> {
|
316
|
+
const clientDir = path.join(path.dirname(fileURLToPath(importMetaUrl)), '../../dist/client/web');
|
317
|
+
const assets = (await fs.promises.readdir(path.join(clientDir, 'assets'), { withFileTypes: true }))
|
318
|
+
.filter((dirent) => dirent.isFile())
|
319
|
+
.map((dirent) => dirent.name);
|
320
|
+
await fs.promises.mkdir(path.join(reportDir, 'assets'), { recursive: true });
|
321
|
+
await fs.promises.copyFile(path.join(clientDir, 'index.html'), path.join(reportDir, 'index.html'));
|
322
|
+
for (const asset of assets) {
|
323
|
+
await fs.promises.copyFile(path.join(clientDir, 'assets', asset), path.join(reportDir, 'assets', asset));
|
324
|
+
}
|
325
|
+
}
|
@@ -218,7 +218,7 @@ export async function start(browser: string, gridUrl: string, config: Config, op
|
|
218
218
|
browserName: baseContext.browserName,
|
219
219
|
workerId: process.pid,
|
220
220
|
images: imagesContext.images,
|
221
|
-
error: serializeError(error),
|
221
|
+
error: error ? serializeError(error) : undefined,
|
222
222
|
duration,
|
223
223
|
attachments: imagesContext.attachments,
|
224
224
|
retries: message.payload.retries,
|
package/src/types.ts
CHANGED
@@ -268,20 +268,17 @@ export interface Config {
|
|
268
268
|
/**
|
269
269
|
* Creevey has two built-in stories providers.
|
270
270
|
*
|
271
|
-
* `
|
272
|
-
* This provider builds and runs storybook in nodejs env, that allows write interaction tests by using Selenium API.
|
273
|
-
* The downside is it depends from project build specific and slightly increases init time.
|
274
|
-
*
|
275
|
-
* `browserStoriesProvider` - The second one is used by default with CSFv3 storybook feature.
|
276
|
-
* It load stories from storybook which is running in browser, like storyshots or loki do it.
|
271
|
+
* `browserStoriesProvider` - Extracts stories directly from the Storybook UI. It loads stories from storybook which is running in browser, like storyshots or loki do it.
|
277
272
|
* The downside of this, you can't use interaction tests in Creevey, unless you use CSFv3.
|
278
273
|
* Where you can define `play` method for each story
|
279
274
|
*
|
275
|
+
* `hybridStoriesProvider` - Combines stories from Storybook with tests from separate files. This is the default provider used in the configuration.
|
276
|
+
*
|
280
277
|
* Usage
|
281
278
|
* ``` typescript
|
282
|
-
* import { nodejsStoriesProvider as provider } from 'creevey'
|
283
|
-
* // or
|
284
279
|
* import { browserStoriesProvider as provider } from 'creevey'
|
280
|
+
* // or
|
281
|
+
* import { hybridStoriesProvider as provider } from 'creevey'
|
285
282
|
*
|
286
283
|
* // Creevey config
|
287
284
|
* module.exports = {
|
@@ -353,7 +350,15 @@ export interface Options {
|
|
353
350
|
_: string[];
|
354
351
|
config?: string;
|
355
352
|
port: number;
|
353
|
+
/**
|
354
|
+
* Run in UI mode with web interface for reviewing test results
|
355
|
+
* When used with `update` flag, enables UI Update Mode for approving screenshots
|
356
|
+
*/
|
356
357
|
ui: boolean;
|
358
|
+
/**
|
359
|
+
* Run in update mode to approve failed tests
|
360
|
+
* When used with `ui` flag, enables UI Update Mode for approving screenshots from browser
|
361
|
+
*/
|
357
362
|
update: boolean | string;
|
358
363
|
debug: boolean;
|
359
364
|
trace: boolean;
|
@@ -529,6 +534,7 @@ export interface CreeveyStatus {
|
|
529
534
|
isRunning: boolean;
|
530
535
|
tests: Partial<Record<string, TestData>>;
|
531
536
|
browsers: string[];
|
537
|
+
isUpdateMode: boolean;
|
532
538
|
}
|
533
539
|
|
534
540
|
export interface CreeveyUpdate {
|