@swedevtools/livedoc-vitest 0.2.0

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.
@@ -0,0 +1,103 @@
1
+ 'use strict';
2
+
3
+ var vitest = require('vitest');
4
+ var livedocVitest = require('@swedevtools/livedoc-vitest');
5
+
6
+ // _src/app/playwright/index.ts
7
+ var playwrightModule;
8
+ var playwrightLoaded = false;
9
+ async function ensurePlaywright() {
10
+ if (playwrightLoaded) return playwrightModule;
11
+ try {
12
+ const moduleName = "playwright";
13
+ playwrightModule = await import(
14
+ /* @vite-ignore */
15
+ moduleName
16
+ );
17
+ playwrightLoaded = true;
18
+ return playwrightModule;
19
+ } catch {
20
+ throw new Error(
21
+ "@swedevtools/livedoc-vitest/playwright requires 'playwright' as a peer dependency.\nInstall it with:\n\n pnpm add -D playwright\n npx playwright install chromium\n"
22
+ );
23
+ }
24
+ }
25
+ var _screenshotIndex = 0;
26
+ function slugify(text) {
27
+ return text.toLowerCase().replace(/['"`]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
28
+ }
29
+ async function screenshot(page, ctx, options) {
30
+ const fullPage = options?.fullPage ?? true;
31
+ const buffer = await page.screenshot({ fullPage });
32
+ const base64 = buffer.toString("base64");
33
+ const name = options?.name ?? `${slugify(ctx.step.title)}-${++_screenshotIndex}`;
34
+ ctx.step.attachScreenshot(base64, name);
35
+ }
36
+ function useBrowser(options) {
37
+ const baseUrl = options?.baseUrl ?? "http://localhost:3000";
38
+ const browserName = options?.browser ?? "chromium";
39
+ const freshContext = options?.freshContextPerScenario ?? false;
40
+ let _browser;
41
+ let _context;
42
+ let _page;
43
+ vitest.beforeAll(async () => {
44
+ const pw = await ensurePlaywright();
45
+ const launcher = pw[browserName];
46
+ _browser = await launcher.launch(options?.launch ?? { headless: true });
47
+ if (!freshContext) {
48
+ _context = await _browser.newContext(options?.context ?? {});
49
+ _page = await _context.newPage();
50
+ }
51
+ _screenshotIndex = 0;
52
+ });
53
+ if (freshContext) {
54
+ livedocVitest.onScenarioStart(async () => {
55
+ _context = await _browser.newContext(options?.context ?? {});
56
+ _page = await _context.newPage();
57
+ });
58
+ livedocVitest.onScenarioEnd(async () => {
59
+ await _context?.close().catch(() => {
60
+ });
61
+ _page = void 0;
62
+ _context = void 0;
63
+ });
64
+ }
65
+ vitest.afterAll(async () => {
66
+ try {
67
+ await _browser?.close();
68
+ } catch {
69
+ } finally {
70
+ _page = void 0;
71
+ _context = void 0;
72
+ _browser = void 0;
73
+ }
74
+ });
75
+ return {
76
+ page: () => {
77
+ if (!_page) {
78
+ throw new Error(
79
+ "Playwright page is not initialized. Ensure useBrowser() is called at module scope and page() is called inside a step."
80
+ );
81
+ }
82
+ return _page;
83
+ },
84
+ context: () => {
85
+ if (!_context) {
86
+ throw new Error("Playwright context is not initialized.");
87
+ }
88
+ return _context;
89
+ },
90
+ browser: () => {
91
+ if (!_browser) {
92
+ throw new Error("Playwright browser is not initialized.");
93
+ }
94
+ return _browser;
95
+ },
96
+ baseUrl
97
+ };
98
+ }
99
+ var usePlaywright = useBrowser;
100
+
101
+ exports.screenshot = screenshot;
102
+ exports.useBrowser = useBrowser;
103
+ exports.usePlaywright = usePlaywright;
@@ -0,0 +1,129 @@
1
+ import { StepContext } from '@swedevtools/livedoc-vitest';
2
+
3
+ /**
4
+ * @swedevtools/livedoc-vitest/playwright
5
+ *
6
+ * Playwright integration for LiveDoc — browser lifecycle management
7
+ * and screenshot helpers for BDD feature specs.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { feature, scenario, given, when, then } from '@swedevtools/livedoc-vitest';
12
+ * import { useBrowser, screenshot } from '@swedevtools/livedoc-vitest/playwright';
13
+ *
14
+ * const { page, baseUrl } = useBrowser({ baseUrl: 'http://localhost:5174' });
15
+ *
16
+ * feature('Checkout Flow', () => {
17
+ * scenario('User adds item to cart', () => {
18
+ * given("user views the product page", async (ctx) => {
19
+ * await page().goto(`${baseUrl}/products/1`);
20
+ * await screenshot(page(), ctx);
21
+ * });
22
+ * });
23
+ * });
24
+ * ```
25
+ */
26
+
27
+ /** Minimal Page interface — the real Playwright Page has many more methods */
28
+ interface PlaywrightPage {
29
+ screenshot(options?: {
30
+ fullPage?: boolean;
31
+ }): Promise<Buffer>;
32
+ goto(url: string, options?: Record<string, unknown>): Promise<unknown>;
33
+ close(): Promise<void>;
34
+ [key: string]: unknown;
35
+ }
36
+ /** Minimal BrowserContext interface */
37
+ interface PlaywrightBrowserContext {
38
+ newPage(): Promise<PlaywrightPage>;
39
+ close(): Promise<void>;
40
+ [key: string]: unknown;
41
+ }
42
+ /** Minimal Browser interface */
43
+ interface PlaywrightBrowser {
44
+ newContext(options?: Record<string, unknown>): Promise<PlaywrightBrowserContext>;
45
+ close(): Promise<void>;
46
+ [key: string]: unknown;
47
+ }
48
+ /** Browser engine to use */
49
+ type BrowserName = "chromium" | "firefox" | "webkit";
50
+ /** Options for useBrowser() / usePlaywright() */
51
+ interface PlaywrightOptions {
52
+ /** Base URL for navigation (default: 'http://localhost:3000') */
53
+ baseUrl?: string;
54
+ /** Browser engine (default: 'chromium') */
55
+ browser?: BrowserName;
56
+ /** Options passed directly to browser.launch() */
57
+ launch?: Record<string, unknown>;
58
+ /** Options passed directly to browser.newContext() */
59
+ context?: Record<string, unknown>;
60
+ /** Create a fresh BrowserContext for each scenario (default: false) */
61
+ freshContextPerScenario?: boolean;
62
+ }
63
+ /** Return type of useBrowser() */
64
+ interface PlaywrightFixture {
65
+ /** Get the current Page instance (call inside steps, not at module scope) */
66
+ page: () => PlaywrightPage;
67
+ /** Get the current BrowserContext */
68
+ context: () => PlaywrightBrowserContext;
69
+ /** Get the Browser instance */
70
+ browser: () => PlaywrightBrowser;
71
+ /** Configured base URL */
72
+ baseUrl: string;
73
+ }
74
+ /** Options for the screenshot() helper */
75
+ interface ScreenshotOptions {
76
+ /** Override auto-generated screenshot name */
77
+ name?: string;
78
+ /** Capture full page scroll height (default: true) */
79
+ fullPage?: boolean;
80
+ }
81
+ /**
82
+ * Capture a screenshot and attach it to the current LiveDoc step.
83
+ *
84
+ * Auto-generates a descriptive filename from the step title if no name is provided.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // Auto-named from step title
89
+ * await screenshot(page(), ctx);
90
+ *
91
+ * // Custom name
92
+ * await screenshot(page(), ctx, { name: 'login-form' });
93
+ * ```
94
+ */
95
+ declare function screenshot(page: PlaywrightPage, ctx: {
96
+ step: StepContext;
97
+ }, options?: ScreenshotOptions): Promise<void>;
98
+ /**
99
+ * Initialize Playwright browser lifecycle for a feature file.
100
+ *
101
+ * Call at **module scope** (outside `feature()` block) to get feature-level
102
+ * browser sharing. The browser launches once and is shared across all
103
+ * scenarios in the file.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * import { useBrowser, screenshot } from '@swedevtools/livedoc-vitest/playwright';
108
+ *
109
+ * const { page, baseUrl } = useBrowser({
110
+ * baseUrl: 'http://localhost:5174',
111
+ * browser: 'chromium',
112
+ * launch: { headless: process.env.CI === 'true' },
113
+ * });
114
+ *
115
+ * feature('My Feature', () => {
116
+ * scenario('My Scenario', () => {
117
+ * given("I open the app", async (ctx) => {
118
+ * await page().goto(baseUrl);
119
+ * await screenshot(page(), ctx);
120
+ * });
121
+ * });
122
+ * });
123
+ * ```
124
+ */
125
+ declare function useBrowser(options?: PlaywrightOptions): PlaywrightFixture;
126
+ /** Alias for useBrowser() */
127
+ declare const usePlaywright: typeof useBrowser;
128
+
129
+ export { type BrowserName, type PlaywrightBrowser, type PlaywrightBrowserContext, type PlaywrightFixture, type PlaywrightOptions, type PlaywrightPage, type ScreenshotOptions, screenshot, useBrowser, usePlaywright };
@@ -0,0 +1,129 @@
1
+ import { StepContext } from '@swedevtools/livedoc-vitest';
2
+
3
+ /**
4
+ * @swedevtools/livedoc-vitest/playwright
5
+ *
6
+ * Playwright integration for LiveDoc — browser lifecycle management
7
+ * and screenshot helpers for BDD feature specs.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { feature, scenario, given, when, then } from '@swedevtools/livedoc-vitest';
12
+ * import { useBrowser, screenshot } from '@swedevtools/livedoc-vitest/playwright';
13
+ *
14
+ * const { page, baseUrl } = useBrowser({ baseUrl: 'http://localhost:5174' });
15
+ *
16
+ * feature('Checkout Flow', () => {
17
+ * scenario('User adds item to cart', () => {
18
+ * given("user views the product page", async (ctx) => {
19
+ * await page().goto(`${baseUrl}/products/1`);
20
+ * await screenshot(page(), ctx);
21
+ * });
22
+ * });
23
+ * });
24
+ * ```
25
+ */
26
+
27
+ /** Minimal Page interface — the real Playwright Page has many more methods */
28
+ interface PlaywrightPage {
29
+ screenshot(options?: {
30
+ fullPage?: boolean;
31
+ }): Promise<Buffer>;
32
+ goto(url: string, options?: Record<string, unknown>): Promise<unknown>;
33
+ close(): Promise<void>;
34
+ [key: string]: unknown;
35
+ }
36
+ /** Minimal BrowserContext interface */
37
+ interface PlaywrightBrowserContext {
38
+ newPage(): Promise<PlaywrightPage>;
39
+ close(): Promise<void>;
40
+ [key: string]: unknown;
41
+ }
42
+ /** Minimal Browser interface */
43
+ interface PlaywrightBrowser {
44
+ newContext(options?: Record<string, unknown>): Promise<PlaywrightBrowserContext>;
45
+ close(): Promise<void>;
46
+ [key: string]: unknown;
47
+ }
48
+ /** Browser engine to use */
49
+ type BrowserName = "chromium" | "firefox" | "webkit";
50
+ /** Options for useBrowser() / usePlaywright() */
51
+ interface PlaywrightOptions {
52
+ /** Base URL for navigation (default: 'http://localhost:3000') */
53
+ baseUrl?: string;
54
+ /** Browser engine (default: 'chromium') */
55
+ browser?: BrowserName;
56
+ /** Options passed directly to browser.launch() */
57
+ launch?: Record<string, unknown>;
58
+ /** Options passed directly to browser.newContext() */
59
+ context?: Record<string, unknown>;
60
+ /** Create a fresh BrowserContext for each scenario (default: false) */
61
+ freshContextPerScenario?: boolean;
62
+ }
63
+ /** Return type of useBrowser() */
64
+ interface PlaywrightFixture {
65
+ /** Get the current Page instance (call inside steps, not at module scope) */
66
+ page: () => PlaywrightPage;
67
+ /** Get the current BrowserContext */
68
+ context: () => PlaywrightBrowserContext;
69
+ /** Get the Browser instance */
70
+ browser: () => PlaywrightBrowser;
71
+ /** Configured base URL */
72
+ baseUrl: string;
73
+ }
74
+ /** Options for the screenshot() helper */
75
+ interface ScreenshotOptions {
76
+ /** Override auto-generated screenshot name */
77
+ name?: string;
78
+ /** Capture full page scroll height (default: true) */
79
+ fullPage?: boolean;
80
+ }
81
+ /**
82
+ * Capture a screenshot and attach it to the current LiveDoc step.
83
+ *
84
+ * Auto-generates a descriptive filename from the step title if no name is provided.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // Auto-named from step title
89
+ * await screenshot(page(), ctx);
90
+ *
91
+ * // Custom name
92
+ * await screenshot(page(), ctx, { name: 'login-form' });
93
+ * ```
94
+ */
95
+ declare function screenshot(page: PlaywrightPage, ctx: {
96
+ step: StepContext;
97
+ }, options?: ScreenshotOptions): Promise<void>;
98
+ /**
99
+ * Initialize Playwright browser lifecycle for a feature file.
100
+ *
101
+ * Call at **module scope** (outside `feature()` block) to get feature-level
102
+ * browser sharing. The browser launches once and is shared across all
103
+ * scenarios in the file.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * import { useBrowser, screenshot } from '@swedevtools/livedoc-vitest/playwright';
108
+ *
109
+ * const { page, baseUrl } = useBrowser({
110
+ * baseUrl: 'http://localhost:5174',
111
+ * browser: 'chromium',
112
+ * launch: { headless: process.env.CI === 'true' },
113
+ * });
114
+ *
115
+ * feature('My Feature', () => {
116
+ * scenario('My Scenario', () => {
117
+ * given("I open the app", async (ctx) => {
118
+ * await page().goto(baseUrl);
119
+ * await screenshot(page(), ctx);
120
+ * });
121
+ * });
122
+ * });
123
+ * ```
124
+ */
125
+ declare function useBrowser(options?: PlaywrightOptions): PlaywrightFixture;
126
+ /** Alias for useBrowser() */
127
+ declare const usePlaywright: typeof useBrowser;
128
+
129
+ export { type BrowserName, type PlaywrightBrowser, type PlaywrightBrowserContext, type PlaywrightFixture, type PlaywrightOptions, type PlaywrightPage, type ScreenshotOptions, screenshot, useBrowser, usePlaywright };
@@ -0,0 +1,99 @@
1
+ import { beforeAll, afterAll } from 'vitest';
2
+ import { onScenarioStart, onScenarioEnd } from '@swedevtools/livedoc-vitest';
3
+
4
+ // _src/app/playwright/index.ts
5
+ var playwrightModule;
6
+ var playwrightLoaded = false;
7
+ async function ensurePlaywright() {
8
+ if (playwrightLoaded) return playwrightModule;
9
+ try {
10
+ const moduleName = "playwright";
11
+ playwrightModule = await import(
12
+ /* @vite-ignore */
13
+ moduleName
14
+ );
15
+ playwrightLoaded = true;
16
+ return playwrightModule;
17
+ } catch {
18
+ throw new Error(
19
+ "@swedevtools/livedoc-vitest/playwright requires 'playwright' as a peer dependency.\nInstall it with:\n\n pnpm add -D playwright\n npx playwright install chromium\n"
20
+ );
21
+ }
22
+ }
23
+ var _screenshotIndex = 0;
24
+ function slugify(text) {
25
+ return text.toLowerCase().replace(/['"`]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
26
+ }
27
+ async function screenshot(page, ctx, options) {
28
+ const fullPage = options?.fullPage ?? true;
29
+ const buffer = await page.screenshot({ fullPage });
30
+ const base64 = buffer.toString("base64");
31
+ const name = options?.name ?? `${slugify(ctx.step.title)}-${++_screenshotIndex}`;
32
+ ctx.step.attachScreenshot(base64, name);
33
+ }
34
+ function useBrowser(options) {
35
+ const baseUrl = options?.baseUrl ?? "http://localhost:3000";
36
+ const browserName = options?.browser ?? "chromium";
37
+ const freshContext = options?.freshContextPerScenario ?? false;
38
+ let _browser;
39
+ let _context;
40
+ let _page;
41
+ beforeAll(async () => {
42
+ const pw = await ensurePlaywright();
43
+ const launcher = pw[browserName];
44
+ _browser = await launcher.launch(options?.launch ?? { headless: true });
45
+ if (!freshContext) {
46
+ _context = await _browser.newContext(options?.context ?? {});
47
+ _page = await _context.newPage();
48
+ }
49
+ _screenshotIndex = 0;
50
+ });
51
+ if (freshContext) {
52
+ onScenarioStart(async () => {
53
+ _context = await _browser.newContext(options?.context ?? {});
54
+ _page = await _context.newPage();
55
+ });
56
+ onScenarioEnd(async () => {
57
+ await _context?.close().catch(() => {
58
+ });
59
+ _page = void 0;
60
+ _context = void 0;
61
+ });
62
+ }
63
+ afterAll(async () => {
64
+ try {
65
+ await _browser?.close();
66
+ } catch {
67
+ } finally {
68
+ _page = void 0;
69
+ _context = void 0;
70
+ _browser = void 0;
71
+ }
72
+ });
73
+ return {
74
+ page: () => {
75
+ if (!_page) {
76
+ throw new Error(
77
+ "Playwright page is not initialized. Ensure useBrowser() is called at module scope and page() is called inside a step."
78
+ );
79
+ }
80
+ return _page;
81
+ },
82
+ context: () => {
83
+ if (!_context) {
84
+ throw new Error("Playwright context is not initialized.");
85
+ }
86
+ return _context;
87
+ },
88
+ browser: () => {
89
+ if (!_browser) {
90
+ throw new Error("Playwright browser is not initialized.");
91
+ }
92
+ return _browser;
93
+ },
94
+ baseUrl
95
+ };
96
+ }
97
+ var usePlaywright = useBrowser;
98
+
99
+ export { screenshot, useBrowser, usePlaywright };