@substrate-system/tapout
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAC,SAAS,GAAC,QAAQ,GAAC,MAAM,CAAA;AA6DnE,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAejD;AAED,wBAAsB,iBAAiB,CACnC,QAAQ,EAAC,MAAM,EACf,OAAO,GAAC;IACJ,OAAO,CAAC,EAAC,MAAM,CAAC;IAChB,aAAa,CAAC,EAAC,OAAO,CAAC;IACvB,OAAO,CAAC,EAAC,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACf,GACR,OAAO,CAAC,IAAI,CAAC,CAoLd"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAC,SAAS,GAAC,QAAQ,GAAC,MAAM,CAAA;AA6DnE,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAejD;AA4BD,wBAAsB,iBAAiB,CACnC,QAAQ,EAAC,MAAM,EACf,OAAO,GAAC;IACJ,OAAO,CAAC,EAAC,MAAM,CAAC;IAChB,aAAa,CAAC,EAAC,OAAO,CAAC;IACvB,OAAO,CAAC,EAAC,gBAAgB,CAAC;IAC1B,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CACf,GACR,OAAO,CAAC,IAAI,CAAC,CAqLd"} |
+12
-1
@@ -59,2 +59,12 @@ var __defProp = Object.defineProperty; | ||
| __name(readStdin, "readStdin"); | ||
| function transformViteEnv(code) { | ||
| let transformed = code; | ||
| transformed = transformed.replace(/import\.meta\.env\.DEV/g, "true"); | ||
| transformed = transformed.replace(/import\.meta\.env\.PROD/g, "false"); | ||
| transformed = transformed.replace(/import\.meta\.env\.MODE/g, '"test"'); | ||
| transformed = transformed.replace(/import\.meta\.env\.BASE_URL/g, '"/"'); | ||
| transformed = transformed.replace(/import\.meta\.env\.SSR/g, "false"); | ||
| return transformed; | ||
| } | ||
| __name(transformViteEnv, "transformViteEnv"); | ||
| async function runTestsInBrowser(testCode, options = {}) { | ||
@@ -78,4 +88,5 @@ const PORT = 8123; | ||
| } else if (pathname === "/test-bundle.js") { | ||
| const transformedCode = transformViteEnv(testCode); | ||
| res.writeHead(200, { "Content-Type": "application/javascript" }); | ||
| res.end(testCode); | ||
| res.end(transformedCode); | ||
| } else { | ||
@@ -82,0 +93,0 @@ res.writeHead(404); |
| { | ||
| "version": 3, | ||
| "sources": ["../src/index.ts"], | ||
| "sourcesContent": ["import { chromium, firefox, webkit, type BrowserType } from 'playwright'\nimport { createServer } from 'node:http'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { promises as fs } from 'node:fs'\nimport { generateHTMLContent } from './util.js'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nexport type SupportedBrowser = 'chromium'|'firefox'|'webkit'|'edge'\n\nconst browsers:Record<SupportedBrowser, BrowserType> = {\n chromium,\n firefox,\n webkit,\n edge: chromium // Edge uses Chromium engine\n}\n\nfunction parseTestLine (line: string) {\n const test = {\n name: '',\n status: 'passed' as 'passed'|'failed'|'skipped',\n duration: Math.floor(Math.random() * 100) + 10, // Mock duration\n error: undefined as string|undefined\n }\n\n // Determine if test passed or failed\n test.status = line.startsWith('ok ') ? 'passed' : 'failed'\n\n // Remove \"ok \" or \"not ok \" prefix and test number\n const remaining = line.replace(/^(not )?ok \\d+\\s*-?\\s*/, '')\n\n // Extract description\n test.name = remaining.trim()\n\n return test\n}\n\nasync function generateHTMLReport (\n testResults:Array<{\n name:string;\n status:'passed' | 'failed' | 'skipped';\n duration?:number;\n error?:string;\n }>,\n browserName:string,\n duration:number,\n outdir?:string,\n outfile?:string\n):Promise<string|null> {\n const html = generateHTMLContent(testResults, browserName, duration)\n\n const filename = outfile || 'index.html'\n\n // If no outfile specified and no outdir specified, output to stdout\n if (!outfile && !outdir) {\n return null // Signal to output to stdout\n }\n\n const outputPath = outdir ? path.join(outdir, filename) : filename\n\n // Create output directory if it doesn't exist\n if (outdir) {\n await fs.mkdir(outdir, { recursive: true })\n }\n\n await fs.writeFile(outputPath, html, 'utf8')\n return outputPath\n}\n\nexport async function readStdin ():Promise<string> {\n return new Promise((resolve, reject) => {\n let data = ''\n\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', chunk => {\n data += chunk\n })\n\n process.stdin.on('end', () => {\n resolve(data)\n })\n\n process.stdin.on('error', reject)\n })\n}\n\nexport async function runTestsInBrowser (\n testCode:string,\n options:{\n timeout?:number;\n customTimeout?:boolean;\n browser?:SupportedBrowser;\n reporter?: 'tap' | 'html';\n outdir?: string;\n outfile?: string;\n } = {}\n):Promise<void> {\n const PORT = 8123\n const timeout = options.timeout || 10000\n const customTimeout = options.customTimeout || false\n const browserType = options.browser || 'chromium'\n const reporter = options.reporter || 'tap'\n\n // Store test results for non-TAP reporters\n const testResults: Array<{\n name: string;\n status: 'passed' | 'failed' | 'skipped';\n duration?: number;\n error?: string;\n }> = []\n const testStartTime = Date.now()\n\n // Custom server to serve static files and dynamic test code\n const server = createServer(async (req, res) => {\n const url = new URL(req.url || '/', `http://localhost:${PORT}`)\n const pathname = url.pathname\n\n try {\n if (pathname === '/' || pathname === '/test-runner.html') {\n // Serve the static HTML file\n const htmlPath = path.join(__dirname, 'test-runner.html')\n const htmlContent = await fs.readFile(htmlPath, 'utf8')\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(htmlContent)\n } else if (pathname === '/test-bundle.js') {\n // Serve the test code\n res.writeHead(200, { 'Content-Type': 'application/javascript' })\n res.end(testCode)\n } else {\n // 404 for other paths\n res.writeHead(404)\n res.end('Not Found')\n }\n } catch (_error) {\n res.writeHead(500)\n res.end('Server Error')\n }\n })\n\n try {\n server.listen(PORT)\n\n const browserOptions = browserType === 'edge' ?\n { channel: 'msedge' as const } :\n (browserType === 'firefox' ?\n {\n headless: true,\n firefoxUserPrefs: {\n 'security.sandbox.content.level': 0,\n 'security.sandbox.plugin.level': 0,\n 'dom.webgpu.enabled': false\n }\n } :\n {})\n\n const browser = await browsers[browserType === 'edge' ?\n 'chromium' :\n browserType].launch(browserOptions)\n const page = await browser.newPage()\n const browserName = browserType === 'edge' ?\n 'edge' :\n browser.browserType().name()\n\n // TAP comment -- which browser is being used\n if (reporter === 'tap') {\n console.log(`# Running tests in ${browserName}`)\n }\n\n let hasErrors = false\n\n page.on('console', msg => {\n const text = msg.text()\n\n // For TAP reporter, output directly to console\n if (reporter === 'tap') {\n console[msg.type()](text)\n }\n\n // Parse and store test results for other reporters\n if (text.startsWith('ok ') || text.startsWith('not ok ')) {\n const testResult = parseTestLine(text)\n if (testResult) {\n testResults.push(testResult)\n }\n }\n\n // TAP failures, errors, specific failure patterns\n // But ignore common browser resource loading messages\n if (\n text.startsWith('not ok') ||\n (\n text.includes('Error:') &&\n !text.includes('Failed to load resource')\n ) ||\n (\n text.includes('Failed') &&\n !text.includes('Failed to load resource')\n ) ||\n text.includes('FAIL') ||\n (\n msg.type() === 'error' &&\n !text.includes('Failed to load resource')\n )\n ) {\n hasErrors = true\n }\n })\n\n page.on('pageerror', error => {\n console.error(`Page error: ${error.message}`)\n hasErrors = true\n })\n\n try {\n await page.goto(`http://localhost:${PORT}/test-runner.html?timeout=${timeout}&custom=${customTimeout}`)\n\n try {\n await page.waitForFunction(\n // @ts-expect-error this runs in a browser\n () => window.testsFinished === true,\n null,\n {\n timeout\n }\n )\n\n // @ts-expect-error this runs in a browser\n const testsFailed = await page.evaluate(() => window.testsFailed)\n\n if (hasErrors || testsFailed) {\n throw new Error('Tests failed')\n } else {\n // Tests passed - no additional output needed for TAP\n }\n } catch (timeoutError:any) {\n if (\n timeoutError.message &&\n timeoutError.message.includes('Timeout')\n ) {\n throw new Error('Tests timed out')\n } else {\n throw timeoutError\n }\n }\n } finally {\n await browser.close()\n server.close()\n\n // Generate HTML report if requested\n if (reporter === 'html') {\n const duration = Date.now() - testStartTime\n const htmlPath = await generateHTMLReport(\n testResults,\n browserName,\n duration,\n options.outdir,\n options.outfile\n )\n\n if (htmlPath === null) {\n // Output HTML to stdout\n const html = generateHTMLContent(\n testResults,\n browserName,\n duration\n )\n console.log(html)\n } else {\n console.log(`HTML report generated: ${htmlPath}`)\n }\n }\n }\n } catch (error) {\n server.close()\n throw error\n }\n}\n"], | ||
| "mappings": "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", | ||
| "sourcesContent": ["import { chromium, firefox, webkit, type BrowserType } from 'playwright'\nimport { createServer } from 'node:http'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { promises as fs } from 'node:fs'\nimport { generateHTMLContent } from './util.js'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nexport type SupportedBrowser = 'chromium'|'firefox'|'webkit'|'edge'\n\nconst browsers:Record<SupportedBrowser, BrowserType> = {\n chromium,\n firefox,\n webkit,\n edge: chromium // Edge uses Chromium engine\n}\n\nfunction parseTestLine (line: string) {\n const test = {\n name: '',\n status: 'passed' as 'passed'|'failed'|'skipped',\n duration: Math.floor(Math.random() * 100) + 10, // Mock duration\n error: undefined as string|undefined\n }\n\n // Determine if test passed or failed\n test.status = line.startsWith('ok ') ? 'passed' : 'failed'\n\n // Remove \"ok \" or \"not ok \" prefix and test number\n const remaining = line.replace(/^(not )?ok \\d+\\s*-?\\s*/, '')\n\n // Extract description\n test.name = remaining.trim()\n\n return test\n}\n\nasync function generateHTMLReport (\n testResults:Array<{\n name:string;\n status:'passed' | 'failed' | 'skipped';\n duration?:number;\n error?:string;\n }>,\n browserName:string,\n duration:number,\n outdir?:string,\n outfile?:string\n):Promise<string|null> {\n const html = generateHTMLContent(testResults, browserName, duration)\n\n const filename = outfile || 'index.html'\n\n // If no outfile specified and no outdir specified, output to stdout\n if (!outfile && !outdir) {\n return null // Signal to output to stdout\n }\n\n const outputPath = outdir ? path.join(outdir, filename) : filename\n\n // Create output directory if it doesn't exist\n if (outdir) {\n await fs.mkdir(outdir, { recursive: true })\n }\n\n await fs.writeFile(outputPath, html, 'utf8')\n return outputPath\n}\n\nexport async function readStdin ():Promise<string> {\n return new Promise((resolve, reject) => {\n let data = ''\n\n process.stdin.setEncoding('utf8')\n process.stdin.on('data', chunk => {\n data += chunk\n })\n\n process.stdin.on('end', () => {\n resolve(data)\n })\n\n process.stdin.on('error', reject)\n })\n}\n\n/**\n * Transform test code to support Vite environment variables.\n * Replaces import.meta.env references with appropriate values.\n */\nfunction transformViteEnv (code:string):string {\n // Replace Vite environment variables with test-appropriate values\n let transformed = code\n\n // Replace import.meta.env.DEV with true (tests run in dev mode)\n transformed = transformed.replace(/import\\.meta\\.env\\.DEV/g, 'true')\n\n // Replace import.meta.env.PROD with false\n transformed = transformed.replace(/import\\.meta\\.env\\.PROD/g, 'false')\n\n // Replace import.meta.env.MODE with \"test\"\n transformed = transformed.replace(/import\\.meta\\.env\\.MODE/g, '\"test\"')\n\n // Replace import.meta.env.BASE_URL with \"/\"\n transformed = transformed.replace(/import\\.meta\\.env\\.BASE_URL/g, '\"/\"')\n\n // Replace import.meta.env.SSR with false\n transformed = transformed.replace(/import\\.meta\\.env\\.SSR/g, 'false')\n\n return transformed\n}\n\nexport async function runTestsInBrowser (\n testCode:string,\n options:{\n timeout?:number;\n customTimeout?:boolean;\n browser?:SupportedBrowser;\n reporter?: 'tap' | 'html';\n outdir?: string;\n outfile?: string;\n } = {}\n):Promise<void> {\n const PORT = 8123\n const timeout = options.timeout || 10000\n const customTimeout = options.customTimeout || false\n const browserType = options.browser || 'chromium'\n const reporter = options.reporter || 'tap'\n\n // Store test results for non-TAP reporters\n const testResults: Array<{\n name: string;\n status: 'passed' | 'failed' | 'skipped';\n duration?: number;\n error?: string;\n }> = []\n const testStartTime = Date.now()\n\n // Custom server to serve static files and dynamic test code\n const server = createServer(async (req, res) => {\n const url = new URL(req.url || '/', `http://localhost:${PORT}`)\n const pathname = url.pathname\n\n try {\n if (pathname === '/' || pathname === '/test-runner.html') {\n // Serve the static HTML file\n const htmlPath = path.join(__dirname, 'test-runner.html')\n const htmlContent = await fs.readFile(htmlPath, 'utf8')\n res.writeHead(200, { 'Content-Type': 'text/html' })\n res.end(htmlContent)\n } else if (pathname === '/test-bundle.js') {\n // Serve the test code with Vite env transformation\n const transformedCode = transformViteEnv(testCode)\n res.writeHead(200, { 'Content-Type': 'application/javascript' })\n res.end(transformedCode)\n } else {\n // 404 for other paths\n res.writeHead(404)\n res.end('Not Found')\n }\n } catch (_error) {\n res.writeHead(500)\n res.end('Server Error')\n }\n })\n\n try {\n server.listen(PORT)\n\n const browserOptions = browserType === 'edge' ?\n { channel: 'msedge' as const } :\n (browserType === 'firefox' ?\n {\n headless: true,\n firefoxUserPrefs: {\n 'security.sandbox.content.level': 0,\n 'security.sandbox.plugin.level': 0,\n 'dom.webgpu.enabled': false\n }\n } :\n {})\n\n const browser = await browsers[browserType === 'edge' ?\n 'chromium' :\n browserType].launch(browserOptions)\n const page = await browser.newPage()\n const browserName = browserType === 'edge' ?\n 'edge' :\n browser.browserType().name()\n\n // TAP comment -- which browser is being used\n if (reporter === 'tap') {\n console.log(`# Running tests in ${browserName}`)\n }\n\n let hasErrors = false\n\n page.on('console', msg => {\n const text = msg.text()\n\n // For TAP reporter, output directly to console\n if (reporter === 'tap') {\n console[msg.type()](text)\n }\n\n // Parse and store test results for other reporters\n if (text.startsWith('ok ') || text.startsWith('not ok ')) {\n const testResult = parseTestLine(text)\n if (testResult) {\n testResults.push(testResult)\n }\n }\n\n // TAP failures, errors, specific failure patterns\n // But ignore common browser resource loading messages\n if (\n text.startsWith('not ok') ||\n (\n text.includes('Error:') &&\n !text.includes('Failed to load resource')\n ) ||\n (\n text.includes('Failed') &&\n !text.includes('Failed to load resource')\n ) ||\n text.includes('FAIL') ||\n (\n msg.type() === 'error' &&\n !text.includes('Failed to load resource')\n )\n ) {\n hasErrors = true\n }\n })\n\n page.on('pageerror', error => {\n console.error(`Page error: ${error.message}`)\n hasErrors = true\n })\n\n try {\n await page.goto(`http://localhost:${PORT}/test-runner.html?timeout=${timeout}&custom=${customTimeout}`)\n\n try {\n await page.waitForFunction(\n // @ts-expect-error this runs in a browser\n () => window.testsFinished === true,\n null,\n {\n timeout\n }\n )\n\n // @ts-expect-error this runs in a browser\n const testsFailed = await page.evaluate(() => window.testsFailed)\n\n if (hasErrors || testsFailed) {\n throw new Error('Tests failed')\n } else {\n // Tests passed - no additional output needed for TAP\n }\n } catch (timeoutError:any) {\n if (\n timeoutError.message &&\n timeoutError.message.includes('Timeout')\n ) {\n throw new Error('Tests timed out')\n } else {\n throw timeoutError\n }\n }\n } finally {\n await browser.close()\n server.close()\n\n // Generate HTML report if requested\n if (reporter === 'html') {\n const duration = Date.now() - testStartTime\n const htmlPath = await generateHTMLReport(\n testResults,\n browserName,\n duration,\n options.outdir,\n options.outfile\n )\n\n if (htmlPath === null) {\n // Output HTML to stdout\n const html = generateHTMLContent(\n testResults,\n browserName,\n duration\n )\n console.log(html)\n } else {\n console.log(`HTML report generated: ${htmlPath}`)\n }\n }\n }\n } catch (error) {\n server.close()\n throw error\n }\n}\n"], | ||
| "mappings": "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", | ||
| "names": [] | ||
| } |
+4
-4
@@ -9,3 +9,3 @@ { | ||
| "src/index.ts": { | ||
| "bytes": 8738, | ||
| "bytes": 9788, | ||
| "imports": [], | ||
@@ -48,3 +48,3 @@ "format": "esm" | ||
| "inputs": {}, | ||
| "bytes": 12902 | ||
| "bytes": 14264 | ||
| }, | ||
@@ -91,6 +91,6 @@ "dist/index.js": { | ||
| "src/index.ts": { | ||
| "bytesInOutput": 5759 | ||
| "bytesInOutput": 6326 | ||
| } | ||
| }, | ||
| "bytes": 5967 | ||
| "bytes": 6534 | ||
| }, | ||
@@ -97,0 +97,0 @@ "dist/util.js.map": { |
+1
-1
| { | ||
| "name": "@substrate-system/tapout", | ||
| "version": "0.0.28", | ||
| "version": "0.0.29", | ||
| "description": "Run tests in a browser from the command line.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+32
-7
@@ -22,2 +22,3 @@ # tapout | ||
| * [`window.testsFinished`](#windowtestsfinished) | ||
| * [Vite environment variables](#vite-environment-variables) | ||
| * [CI](#ci) | ||
@@ -60,9 +61,7 @@ * [Generate HTML reports](#generate-html-reports) | ||
| - **Cross-browser testing**: Run tests in Chrome, Firefox, Safari (WebKit), or Edge | ||
| - **Smart timeout handling**: Respects custom timeouts with intelligent | ||
| auto-finish behavior | ||
| - **Comprehensive error detection**: Automatically catches unhandled promise | ||
| rejections, uncaught exceptions, and console errors | ||
| - **Beautiful HTML reports**: Generate responsive HTML reports perfect for | ||
| CI/CD or sharing | ||
| - **Cross-browser testing**: Run tests in Chrome, Firefox, Safari (WebKit), | ||
| or Edge | ||
| - **Vite support**: Automatic support for `import.meta.env` variables | ||
| - **Smart timeout handling**: Use custom timeouts or auto-timeout | ||
| - **Beautiful HTML reports**: Generate HTML reports perfect for CI | ||
| - **TAP compatible**: Standard TAP output works with any TAP formatter | ||
@@ -112,2 +111,28 @@ - **Zero configuration**: Just pipe JavaScript into this command | ||
| ### Vite environment variables | ||
| Vite environment variables, like `import.meta.env.DEV` are defined, so your | ||
| tests wont break if you use them in your application code. | ||
| * `import.meta.env.DEV` - `true` (tests run in development mode) | ||
| * `import.meta.env.PROD` - `false` | ||
| * `import.meta.env.MODE` - `"test"` | ||
| * `import.meta.env.BASE_URL` - `"/"` | ||
| * `import.meta.env.SSR` - `false` | ||
| #### Example | ||
| ```js | ||
| // Your Vite app code can use these environment variables | ||
| if (import.meta.env.DEV) { | ||
| console.log('Running in development mode') | ||
| } | ||
| const apiUrl = import.meta.env.DEV ? | ||
| 'http://localhost:3000/api' : | ||
| 'https://production.api.com' | ||
| ``` | ||
| ### CI | ||
@@ -114,0 +139,0 @@ |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
72794
3.58%656
1.71%300
9.09%