From 54636f287024f7a07b6b03aa4ce153bcc517d917 Mon Sep 17 00:00:00 2001 From: Ian Crutcher Date: Tue, 13 Oct 2026 18:14:41 -0500 Subject: [PATCH] Add no-recording option --- package-lock.json | 91 +++-------- packages/telescope/README.md | 13 ++ packages/telescope/__tests__/browsers.test.ts | 4 +- .../telescope/__tests__/emulation.test.ts | 4 +- .../telescope/__tests__/recording.test.ts | 141 ++++++++++++++++++ packages/telescope/src/browsers.ts | 11 +- packages/telescope/src/config.ts | 1 + packages/telescope/src/defaultOptions.ts | 2 + packages/telescope/src/index.ts | 15 ++ packages/telescope/src/templates/test.ejs | 4 + packages/telescope/src/testRunner.ts | 25 +++- packages/telescope/src/types.ts | 9 +- 12 files changed, 237 insertions(+), 83 deletions(-) create mode 100644 packages/telescope/__tests__/recording.test.ts diff --git a/package-lock.json b/package-lock.json index 02244cd9..ccf35eef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -333,6 +333,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1249,7 +1250,8 @@ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.3.tgz", "integrity": "sha512-ichuWTgtd4mOM1G4SpyGJa5trT03lWbMypDV0fUXUCXg5hiHqVAz/bZyV68NqmkLB7WcYmj1RMJVSp8HV/v/ZQ==", "devOptional": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@electric-sql/pglite-socket": { "version": "0.1.3", @@ -1274,28 +1276,6 @@ "@electric-sql/pglite": "0.4.3" } }, - "node_modules/@emnapi/core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", - "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", @@ -3934,6 +3914,7 @@ "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.18.0" } @@ -3943,6 +3924,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3952,6 +3934,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4014,6 +3997,7 @@ "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.65.0", "@typescript-eslint/types": "8.65.0", @@ -4531,6 +4515,7 @@ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4723,6 +4708,7 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-7.1.3.tgz", "integrity": "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA==", "license": "MIT", + "peer": true, "dependencies": { "@astrojs/compiler-rs": "^0.3.1", "@astrojs/internal-helpers": "0.10.1", @@ -4936,6 +4922,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.44", "caniuse-lite": "^1.0.30001806", @@ -5880,6 +5867,7 @@ "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", "dev": true, "license": "MIT", + "peer": true, "workspaces": [ "packages/*" ], @@ -7059,6 +7047,7 @@ "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -7819,9 +7808,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7838,9 +7824,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7857,9 +7840,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7876,9 +7856,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7895,9 +7872,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7914,9 +7888,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7933,9 +7904,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7952,9 +7920,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -7971,9 +7936,6 @@ "cpu": [ "arm" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -7996,9 +7958,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8021,9 +7980,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8046,9 +8002,6 @@ "cpu": [ "riscv64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8071,9 +8024,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8096,9 +8046,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8121,9 +8068,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8146,9 +8090,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "Apache-2.0", "optional": true, "os": [ @@ -8965,6 +8906,7 @@ "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8992,6 +8934,7 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "7.9.1", "@prisma/dev": "0.24.17", @@ -9153,6 +9096,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9162,6 +9106,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -10096,6 +10041,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10143,6 +10089,7 @@ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "license": "MIT", + "peer": true, "dependencies": { "pathe": "^2.0.3" } @@ -10479,6 +10426,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", @@ -10780,6 +10728,7 @@ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.123.0.tgz", "integrity": "sha512-VXo2I1oa0x9aGAKIFPRSQPqTh0RBY5Ktl44YOhNmsJQFUdJKDA2vVTU6Xj+FC2koll6orJqWZN8jbXVIk9O67Q==", "license": "MIT OR Apache-2.0", + "peer": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", diff --git a/packages/telescope/README.md b/packages/telescope/README.md index 84e301b0..043ac6cf 100644 --- a/packages/telescope/README.md +++ b/packages/telescope/README.md @@ -65,6 +65,7 @@ Options: --width Viewport width, in pixels. If both width and device are provided, the width value will override device emulation viewport width. --height Viewport height, in pixels. If both height and device are provided, the height value will override device emulation viewport height. --frameRate Filmstrip frame rate, in frames per second (default: 1) + --no-recording Disable video recording, final screenshot, and filmstrip (cannot be combined with --uploadUrl) --disableJS Disable JavaScript (default: false) --debug Output debug lines (default: false) --auth Basic HTTP authentication (Expects: {"username": "", "password": ""}) (default: false) @@ -101,6 +102,18 @@ And supports the following browsers: - [Safari](https://www.apple.com/safari/)/[Webkit](https://webkit.org/) - `safari` - [Firefox](https://www.firefox.com/) - `firefox` +### Disabling Recording + +By default, Telescope records a video of the page load, takes a final screenshot, and generates a filmstrip. Pass `--no-recording` (or `recording: false` programmatically) to skip all three. HAR, metrics, and other results are still collected. Recording is required for uploads, so `--uploadUrl` cannot be combined with `--no-recording`. + +``` +telescope -u https://www.example.com --no-recording +``` + +```ts +await launchTest({ url: 'https://www.example.com', recording: false }); +``` + ### Custom Timeout You can set a custom timeout by passing the desired timeout in milliseconds using the `--timeout` parameter. Defaults to 30000, or 30 seconds. diff --git a/packages/telescope/__tests__/browsers.test.ts b/packages/telescope/__tests__/browsers.test.ts index 65514df0..7b77323f 100644 --- a/packages/telescope/__tests__/browsers.test.ts +++ b/packages/telescope/__tests__/browsers.test.ts @@ -66,8 +66,8 @@ describe.each(browsers)('Basic configuration tests: %s', browser => { }; const config = new BrowserConfig().getBrowserConfig(browser, options); expect(config && typeof config === 'object').toBe(true); - expect(config.recordVideo.size.width === 500).toBe(true); - expect(config.recordVideo.size.height === 700).toBe(true); + expect(config.recordVideo?.size.width === 500).toBe(true); + expect(config.recordVideo?.size.height === 700).toBe(true); }); test('Setting a viewport with a string throws an error', () => { diff --git a/packages/telescope/__tests__/emulation.test.ts b/packages/telescope/__tests__/emulation.test.ts index 39b4e7ba..15ef5abd 100644 --- a/packages/telescope/__tests__/emulation.test.ts +++ b/packages/telescope/__tests__/emulation.test.ts @@ -132,10 +132,10 @@ describe.each(browsers)( expect(config.viewport.height).toBe(3344); }); it(`Setting device emulation sets recordVideo width to override value for browser: ${browser}`, () => { - expect(config.recordVideo.size.width).toBe(1122); + expect(config.recordVideo?.size.width).toBe(1122); }); it(`Setting device emulation sets recordVideo height to override value for browser: ${browser}`, () => { - expect(config.recordVideo.size.height).toBe(3344); + expect(config.recordVideo?.size.height).toBe(3344); }); }, ); diff --git a/packages/telescope/__tests__/recording.test.ts b/packages/telescope/__tests__/recording.test.ts new file mode 100644 index 00000000..d6d7e196 --- /dev/null +++ b/packages/telescope/__tests__/recording.test.ts @@ -0,0 +1,141 @@ +import { launchTest } from '../src/index.js'; +import { describe, expect, it, test, beforeAll, afterAll } from 'vitest'; +import { spawnSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; + +import { BrowserConfig } from '../src/browsers.js'; +import type { SuccessfulTestResult } from '../src/types.js'; +import { retrieveConfig, cleanupTestDirectory } from './helpers.js'; + +const browsers = BrowserConfig.getBrowsers(); + +function hasVideo(resultsPath: string): boolean { + return fs.readdirSync(resultsPath).some(f => /\.(webm|mp4)$/i.test(f)); +} + +describe('Recording flag: browser config', () => { + test('recordVideo is present by default', () => { + const config = new BrowserConfig().getBrowserConfig('chrome', { + url: 'https://www.example.com', + }); + expect(config.recordVideo).toBeDefined(); + }); + + test('recordVideo is removed when recording is false', () => { + const config = new BrowserConfig().getBrowserConfig('chrome', { + url: 'https://www.example.com', + recording: false, + }); + expect(config.recordVideo).toBeUndefined(); + }); +}); + +describe('Recording flag: upload validation', () => { + it('launchTest rejects uploadUrl when recording is false', async () => { + const result = await launchTest({ + url: 'https://www.example.com', + recording: false, + uploadUrl: 'https://api.example.com/upload', + }); + expect(result.success).toBe(false); + if (!result.success) { + expect(result.error).toMatch(/requires recording/); + } + }); + + it('CLI rejects --uploadUrl combined with --no-recording', () => { + const output = spawnSync('node', [ + 'dist/src/cli.js', + '--no-recording', + '--uploadUrl', + 'https://api.example.com/upload', + 'https://www.example.com', + ]); + expect(output.status).toBe(1); + expect(output.stderr.toString()).toMatch(/requires recording/); + }); +}); + +describe('Recording flag: CLI config', () => { + function readRecordingFromDryRun(args: string[]): boolean | undefined { + let testId: string | undefined; + try { + const output = spawnSync('node', [ + 'dist/src/cli.js', + '--dry', + ...args, + 'https://www.example.com', + ]); + const match = output.stdout.toString().match(/Test ID:(.*)/); + if (match && match.length > 1) { + testId = match[1].trim(); + return retrieveConfig(testId)?.options.recording; + } + } finally { + cleanupTestDirectory(testId); + } + } + + it('defaults to true', () => { + expect(readRecordingFromDryRun([])).toBe(true); + }); + + it('--no-recording sets recording to false', () => { + expect(readRecordingFromDryRun(['--no-recording'])).toBe(false); + }); +}); + +describe.each(browsers)('Recording disabled (%s)', browser => { + let result: SuccessfulTestResult; + + beforeAll(async () => { + const testResult = await launchTest({ + url: 'https://www.example.com', + browser, + recording: false, + html: true, + }); + if (!testResult.success) throw new Error(testResult.error); + result = testResult; + }, 120000); + + afterAll(() => { + if (result) cleanupTestDirectory(result.testId); + }); + + it('does not record a video', () => { + expect(hasVideo(result.resultsPath)).toBe(false); + }); + + it('does not take a screenshot', () => { + expect(fs.existsSync(path.join(result.resultsPath, 'screenshot.png'))).toBe( + false, + ); + }); + + it('does not generate a filmstrip', () => { + expect(fs.existsSync(path.join(result.resultsPath, 'filmstrip'))).toBe( + false, + ); + }); + + it('still generates the HTML report', () => { + expect(fs.existsSync(path.join(result.resultsPath, 'index.html'))).toBe( + true, + ); + }); + + it('still produces the HAR and metrics', () => { + expect(fs.existsSync(path.join(result.resultsPath, 'pageload.har'))).toBe( + true, + ); + expect(fs.existsSync(path.join(result.resultsPath, 'metrics.json'))).toBe( + true, + ); + }); + + it('saves recording: false in config.json', () => { + expect(retrieveConfig(result.testId)?.options.recording).toBe(false); + }); +}); diff --git a/packages/telescope/src/browsers.ts b/packages/telescope/src/browsers.ts index 45793026..8a8a71e2 100644 --- a/packages/telescope/src/browsers.ts +++ b/packages/telescope/src/browsers.ts @@ -285,8 +285,15 @@ class BrowserConfig { browserConfig.viewport.width = resolvedWidth; browserConfig.viewport.height = resolvedHeight; - browserConfig.recordVideo.size.width = resolvedWidth; - browserConfig.recordVideo.size.height = resolvedHeight; + if (options.recording === false) { + // Playwright records video whenever recordVideo is present + delete browserConfig.recordVideo; + } else { + browserConfig.recordVideo = { + dir: this.defaultBrowserOptions.recordVideo?.dir ?? './recordings', + size: { width: resolvedWidth, height: resolvedHeight }, + }; + } // collect device info and insert device emulation settings into browser config if (options.device) { diff --git a/packages/telescope/src/config.ts b/packages/telescope/src/config.ts index 9e7e72c4..96c69b1e 100644 --- a/packages/telescope/src/config.ts +++ b/packages/telescope/src/config.ts @@ -50,6 +50,7 @@ export function normalizeCLIConfig(options: CLIOptions): LaunchOptions { width: options.width, height: options.height, frameRate: options.frameRate ?? DEFAULT_OPTIONS.frameRate, + recording: options.recording ?? DEFAULT_OPTIONS.recording, timeout: options.timeout ?? DEFAULT_OPTIONS.timeout, blockDomains: options.blockDomains || DEFAULT_OPTIONS.blockDomains, block: options.block || DEFAULT_OPTIONS.block, diff --git a/packages/telescope/src/defaultOptions.ts b/packages/telescope/src/defaultOptions.ts index 205149c1..be1edeaa 100644 --- a/packages/telescope/src/defaultOptions.ts +++ b/packages/telescope/src/defaultOptions.ts @@ -12,6 +12,8 @@ export const DEFAULT_OPTIONS: DefaultOptions = { browser: 'chrome', // Filmstrip capture rate (frames per second) frameRate: 1, + // Record video, final screenshot, and filmstrip + recording: true, // Test timeout in milliseconds timeout: 30000, // Domains to block from loading (request blocking) diff --git a/packages/telescope/src/index.ts b/packages/telescope/src/index.ts index 2696f8bd..b6517134 100644 --- a/packages/telescope/src/index.ts +++ b/packages/telescope/src/index.ts @@ -120,7 +120,16 @@ async function executeTest( ...DEFAULT_OPTIONS, ...options, baseline: options.baseline ?? DEFAULT_OPTIONS.baseline, + recording: options.recording ?? DEFAULT_OPTIONS.recording, }; + + // Uploads rely on recording artifacts (screenshot, video, filmstrip), + // so uploading is not allowed when recording is disabled. + if (!config.recording && config.uploadUrl) { + throw new Error( + 'Uploading results requires recording to be enabled (uploadUrl cannot be combined with recording: false / --no-recording)', + ); + } const browserConfig = new BrowserConfig().getBrowserConfig( config.browser || 'chrome', config, @@ -319,6 +328,12 @@ export default function browserAgent(): void { .default(DEFAULT_OPTIONS.frameRate) .argParser(v => parseNumeric(PositiveIntSchema, v, '--frameRate')), ) + .addOption( + new Option( + '--no-recording', + 'Disable video recording, final screenshot, and filmstrip (cannot be combined with --uploadUrl)', + ), + ) .addOption( new Option( '--baseline', diff --git a/packages/telescope/src/templates/test.ejs b/packages/telescope/src/templates/test.ejs index c364bc06..5f750930 100644 --- a/packages/telescope/src/templates/test.ejs +++ b/packages/telescope/src/templates/test.ejs @@ -97,6 +97,7 @@ UX Speed test report generated using Telescope v1.0.0 + <% if (resultAssets.filmstrip && resultAssets.filmstrip.length) { %>
<% for (let frame of resultAssets.filmstrip) { %> @@ -108,6 +109,7 @@
+ <% } %>
@@ -133,6 +135,7 @@
  • Console log: console.json
  • + <% if (resultAssets.videoFile) { %> + <% } %>
    diff --git a/packages/telescope/src/testRunner.ts b/packages/telescope/src/testRunner.ts index 52f7a82e..11399814 100644 --- a/packages/telescope/src/testRunner.ts +++ b/packages/telescope/src/testRunner.ts @@ -73,6 +73,7 @@ class TestRunner { requests: RequestData[] = []; telescopeIdToRequestId: Record = {}; resultAssets: ResultAssets = { + filmstrip: [], filmstripFiles: [], videoFile: null, }; @@ -101,7 +102,17 @@ class TestRunner { this.selectedBrowser.recordHar.path = this.paths['results'] + '/pageload.har'; - this.selectedBrowser.recordVideo.dir = this.paths['results']; + if (this.selectedBrowser.recordVideo) { + this.selectedBrowser.recordVideo.dir = this.paths['results']; + } + } + + /** + * Whether video recording, the final screenshot, and filmstrip are enabled. + * @returns true unless `recording` was explicitly set to false + */ + get recordingEnabled(): boolean { + return this.options.recording !== false; } /** @@ -411,9 +422,11 @@ class TestRunner { } } // grab our screenshot - await this.page.screenshot({ - path: this.paths['results'] + '/screenshot.png', - }); + if (this.recordingEnabled) { + await this.page.screenshot({ + path: this.paths['results'] + '/screenshot.png', + }); + } //grab the videoname const video = this.page.video(); @@ -778,7 +791,9 @@ class TestRunner { } //create our filmstrip - await this.createFilmStrip(); + if (this.recordingEnabled) { + await this.createFilmStrip(); + } if (this.options.html) { // Generate HTML report diff --git a/packages/telescope/src/types.ts b/packages/telescope/src/types.ts index 05aaefb2..4f1aee7e 100644 --- a/packages/telescope/src/types.ts +++ b/packages/telescope/src/types.ts @@ -132,7 +132,7 @@ export interface BrowserConfigOptions { headless: boolean; viewport: { width: number; height: number }; recordHar: { path: string }; - recordVideo: { dir: string; size: { width: number; height: number } }; + recordVideo?: { dir: string; size: { width: number; height: number } }; args?: string[]; ignoreDefaultArgs?: string[]; firefoxUserPrefs?: Record; @@ -184,6 +184,11 @@ export interface LaunchOptions { width?: number; height?: number; frameRate?: number; + /** + * Record video, take a final screenshot, and generate a filmstrip. + * Defaults to true. Cannot be disabled when `uploadUrl` is set. + */ + recording?: boolean; disableJS?: boolean; debug?: boolean; auth?: HTTPCredentials | false; @@ -212,6 +217,7 @@ export interface DefaultOptions { width?: number; height?: number; frameRate: number; + recording: boolean; timeout: number; blockDomains: string[]; block: string[]; @@ -857,6 +863,7 @@ export interface CLIOptions { width?: number; height?: number; frameRate?: number; + recording?: boolean; disableJS?: boolean; debug?: boolean; auth?: HTTPCredentials | false;