@lvce-editor/test-with-playwright-worker 0.0.0-dev
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/index.js +6 -0
- package/package.json +25 -0
- package/src/parts/Assert/Assert.js +59 -0
- package/src/parts/AssertionError/AssertionError.js +6 -0
- package/src/parts/Callback/Callback.js +39 -0
- package/src/parts/Character/Character.js +11 -0
- package/src/parts/CleanStack/CleanStack.js +93 -0
- package/src/parts/Cli/Cli.js +8 -0
- package/src/parts/CliCommandType/CliCommandType.js +2 -0
- package/src/parts/Command/Command.js +9 -0
- package/src/parts/CommandMap/CommandMap.js +6 -0
- package/src/parts/CommandNotFoundError/CommandNotFoundError.js +9 -0
- package/src/parts/CommandState/CommandState.js +17 -0
- package/src/parts/EncodingType/EncodingType.js +1 -0
- package/src/parts/ErrorCodes/ErrorCodes.js +19 -0
- package/src/parts/GetErrorResponse/GetErrorResponse.js +56 -0
- package/src/parts/GetPort/GetPort.js +5 -0
- package/src/parts/GetResponse/GetResponse.js +12 -0
- package/src/parts/GetServerPath/GetServerPath.js +4 -0
- package/src/parts/GetSuccessResponse/GetSuccessResponse.js +12 -0
- package/src/parts/GetTestState/GetTestState.js +24 -0
- package/src/parts/GetTests/GetTests.js +22 -0
- package/src/parts/HandleCliArgs/HandleCliArgs.js +19 -0
- package/src/parts/HandleIpc/HandleIpc.js +13 -0
- package/src/parts/Id/Id.js +7 -0
- package/src/parts/IpcChild/IpcChild.js +9 -0
- package/src/parts/IpcChildModule/IpcChildModule.js +12 -0
- package/src/parts/IpcChildType/IpcChildType.js +13 -0
- package/src/parts/IpcChildWithNodeForkedProcess/IpcChildWithNodeForkedProcess.js +41 -0
- package/src/parts/IpcChildWithNodeWorker/IpcChildWithNodeWorker.js +27 -0
- package/src/parts/IpcError/IpcError.js +6 -0
- package/src/parts/IsTestFile/IsTestFile.js +6 -0
- package/src/parts/JoinLines/JoinLines.js +5 -0
- package/src/parts/Json/Json.js +17 -0
- package/src/parts/JsonError/JsonError.js +64 -0
- package/src/parts/JsonParsingError/JsonParsingError.js +17 -0
- package/src/parts/JsonRpc/JsonRpc.js +35 -0
- package/src/parts/JsonRpcErrorCode/JsonRpcErrorCode.js +2 -0
- package/src/parts/JsonRpcVersion/JsonRpcVersion.js +1 -0
- package/src/parts/Logger/Logger.js +43 -0
- package/src/parts/Main/Main.js +36 -0
- package/src/parts/NoTestFilesFoundError/NoTestFilesFoundError.js +8 -0
- package/src/parts/PrettyError/PrettyError.js +139 -0
- package/src/parts/PrintPrettyError/PrintPrettyError.js +5 -0
- package/src/parts/Process/Process.js +20 -0
- package/src/parts/ProcessListeners/ProcessListeners.js +3 -0
- package/src/parts/RunAllTests/RunAllTests.js +45 -0
- package/src/parts/RunTest/RunTest.js +37 -0
- package/src/parts/RunTests/RunTests.js +44 -0
- package/src/parts/SetupTests/SetupTests.js +25 -0
- package/src/parts/Signal/Signal.js +1 -0
- package/src/parts/SplitLines/SplitLines.js +5 -0
- package/src/parts/StartBrowser/StartBrowser.js +21 -0
- package/src/parts/StartServer/StartServer.js +29 -0
- package/src/parts/TearDownTests/TearDownTests.js +6 -0
- package/src/parts/TestOverlayState/TestOverlayState.js +3 -0
- package/src/parts/TestState/TestState.js +3 -0
- package/src/parts/TestWorkerCommandType/TestWorkerCommandType.js +1 -0
- package/src/workerMain.js +3 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { codeFrameColumns } from '@babel/code-frame'
|
|
2
|
+
import { LinesAndColumns } from 'lines-and-columns'
|
|
3
|
+
|
|
4
|
+
// parsing error handling based on https://github.com/sindresorhus/parse-json/blob/main/index.js
|
|
5
|
+
|
|
6
|
+
const emptyError = {
|
|
7
|
+
message: '',
|
|
8
|
+
stack: '',
|
|
9
|
+
codeFrame: '',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const getErrorPropsFromError = (error, string, filePath) => {
|
|
13
|
+
const indexMatch = error.message.match(/in JSON at position (\d+)/)
|
|
14
|
+
if (indexMatch && indexMatch.length > 0) {
|
|
15
|
+
const lines = new LinesAndColumns(string)
|
|
16
|
+
const index = Number(indexMatch[1])
|
|
17
|
+
const location = lines.locationForIndex(index)
|
|
18
|
+
if (location) {
|
|
19
|
+
const line = location.line + 1
|
|
20
|
+
const column = location.column + 1
|
|
21
|
+
const codeFrame = codeFrameColumns(string, { start: { line, column } }, { highlightCode: false })
|
|
22
|
+
return {
|
|
23
|
+
codeFrame,
|
|
24
|
+
message: 'Json Parsing Error',
|
|
25
|
+
stack: ` at ${filePath}:${line}:${column}`,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (string.length === 0) {
|
|
30
|
+
return {
|
|
31
|
+
codeFrame: ``,
|
|
32
|
+
message: 'Json Parsing Error: Cannot parse empty string',
|
|
33
|
+
stack: ` at ${filePath}`,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (error.message === 'Unexpected end of JSON input') {
|
|
38
|
+
const lines = new LinesAndColumns(string)
|
|
39
|
+
const index = string.length - 1
|
|
40
|
+
const location = lines.locationForIndex(index)
|
|
41
|
+
if (location) {
|
|
42
|
+
const codeFrame = codeFrameColumns(string, { start: { line: location.line + 1, column: location.column + 1 } }, { highlightCode: false })
|
|
43
|
+
return {
|
|
44
|
+
codeFrame,
|
|
45
|
+
message: 'Json Parsing Error',
|
|
46
|
+
stack: ` at ${filePath}`,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
codeFrame: ``,
|
|
52
|
+
message: 'Json Parsing Error',
|
|
53
|
+
stack: ` at ${filePath}`,
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const getErrorProps = (string, filePath) => {
|
|
58
|
+
try {
|
|
59
|
+
JSON.parse(string)
|
|
60
|
+
return emptyError
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return getErrorPropsFromError(error, string, filePath)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as JoinLines from '../JoinLines/JoinLines.js'
|
|
2
|
+
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
3
|
+
|
|
4
|
+
export class JsonParsingError extends Error {
|
|
5
|
+
constructor(message, codeFrame, stack) {
|
|
6
|
+
super(message)
|
|
7
|
+
this.name = 'JsonParsingError'
|
|
8
|
+
if (codeFrame) {
|
|
9
|
+
this.codeFrame = codeFrame
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (stack) {
|
|
13
|
+
const parentStack = JoinLines.joinLines(SplitLines.splitLines(this.stack).slice(1))
|
|
14
|
+
this.stack = this.name + ': ' + this.message + '\n' + stack + '\n' + parentStack
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as Callback from '../Callback/Callback.js'
|
|
2
|
+
import * as JsonRpcVersion from '../JsonRpcVersion/JsonRpcVersion.js'
|
|
3
|
+
|
|
4
|
+
export const send = (transport, method, ...params) => {
|
|
5
|
+
transport.send({
|
|
6
|
+
jsonrpc: JsonRpcVersion.Two,
|
|
7
|
+
method,
|
|
8
|
+
params,
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const invoke = (ipc, method, ...params) => {
|
|
13
|
+
const { id, promise } = Callback.registerPromise()
|
|
14
|
+
ipc.send({
|
|
15
|
+
jsonrpc: JsonRpcVersion.Two,
|
|
16
|
+
method,
|
|
17
|
+
params,
|
|
18
|
+
id,
|
|
19
|
+
})
|
|
20
|
+
return promise
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const invokeAndTransfer = (ipc, handle, method, ...params) => {
|
|
24
|
+
const { id, promise } = Callback.registerPromise()
|
|
25
|
+
ipc.sendAndTransfer(
|
|
26
|
+
{
|
|
27
|
+
jsonrpc: JsonRpcVersion.Two,
|
|
28
|
+
method,
|
|
29
|
+
params,
|
|
30
|
+
id,
|
|
31
|
+
},
|
|
32
|
+
handle
|
|
33
|
+
)
|
|
34
|
+
return promise
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const Two = '2.0'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// TODO mock this module when used in unit tests
|
|
2
|
+
|
|
3
|
+
const state = {
|
|
4
|
+
/**
|
|
5
|
+
* @type {Console|undefined}
|
|
6
|
+
*/
|
|
7
|
+
console: undefined,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const createConsole = () => {
|
|
11
|
+
return console
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const getOrCreateLogger = () => {
|
|
15
|
+
if (!state.console) {
|
|
16
|
+
state.console = createConsole()
|
|
17
|
+
}
|
|
18
|
+
return state.console
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const log = (...args) => {
|
|
22
|
+
const logger = getOrCreateLogger()
|
|
23
|
+
logger.log(...args)
|
|
24
|
+
console.log(...args)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const info = (...args) => {
|
|
28
|
+
const logger = getOrCreateLogger()
|
|
29
|
+
logger.info(...args)
|
|
30
|
+
console.info(...args)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const warn = (...args) => {
|
|
34
|
+
const logger = getOrCreateLogger()
|
|
35
|
+
logger.warn(...args)
|
|
36
|
+
console.warn(...args)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const error = (...args) => {
|
|
40
|
+
const logger = getOrCreateLogger()
|
|
41
|
+
logger.error(...args)
|
|
42
|
+
console.error(...args)
|
|
43
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as CommandMap from '../CommandMap/CommandMap.js'
|
|
2
|
+
import * as CommandState from '../CommandState/CommandState.js'
|
|
3
|
+
import * as HandleIpc from '../HandleIpc/HandleIpc.js'
|
|
4
|
+
import * as IpcChild from '../IpcChild/IpcChild.js'
|
|
5
|
+
import * as IpcChildType from '../IpcChildType/IpcChildType.js'
|
|
6
|
+
import * as Process from '../Process/Process.js'
|
|
7
|
+
import * as ProcessListeners from '../ProcessListeners/ProcessListeners.js'
|
|
8
|
+
|
|
9
|
+
const handleDisconnect = () => {
|
|
10
|
+
console.log('[test-worker] disconnected')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const handleExit = () => {
|
|
14
|
+
console.log('[test-worker] exiting')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const handleSigint = () => {
|
|
18
|
+
console.log('[test-worker] sigint')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const handleSigTerm = () => {
|
|
22
|
+
console.log('[test-worker] sigterm')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const main = async () => {
|
|
26
|
+
Process.on('disconnect', handleDisconnect)
|
|
27
|
+
Process.on('exit', handleExit)
|
|
28
|
+
Process.on('SIGINT', handleSigint)
|
|
29
|
+
Process.on('SIGTERM', handleSigTerm)
|
|
30
|
+
Process.on('uncaughtExceptionMonitor', ProcessListeners.handleUncaughtExceptionMonitor)
|
|
31
|
+
CommandState.registerCommands(CommandMap.commandMap)
|
|
32
|
+
const ipc = await IpcChild.listen({
|
|
33
|
+
method: IpcChildType.Auto(),
|
|
34
|
+
})
|
|
35
|
+
HandleIpc.handleIpc(ipc)
|
|
36
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { codeFrameColumns } from '@babel/code-frame'
|
|
2
|
+
import { LinesAndColumns } from 'lines-and-columns'
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { AssertionError } from '../AssertionError/AssertionError.js'
|
|
6
|
+
import * as CleanStack from '../CleanStack/CleanStack.js'
|
|
7
|
+
import * as EncodingType from '../EncodingType/EncodingType.js'
|
|
8
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
9
|
+
import * as JoinLines from '../JoinLines/JoinLines.js'
|
|
10
|
+
import * as Json from '../Json/Json.js'
|
|
11
|
+
import * as SplitLines from '../SplitLines/SplitLines.js'
|
|
12
|
+
|
|
13
|
+
const getActualPath = (fileUri) => {
|
|
14
|
+
if (fileUri.startsWith('file://')) {
|
|
15
|
+
return fileURLToPath(fileUri)
|
|
16
|
+
}
|
|
17
|
+
return fileUri
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const RE_MODULE_NOT_FOUND_STACK = /Cannot find package '([^']+)' imported from (.+)$/
|
|
21
|
+
|
|
22
|
+
const prepareModuleNotFoundError = (error) => {
|
|
23
|
+
const { message } = error
|
|
24
|
+
const match = message.match(RE_MODULE_NOT_FOUND_STACK)
|
|
25
|
+
if (!match) {
|
|
26
|
+
return {
|
|
27
|
+
message,
|
|
28
|
+
stack: error.stack,
|
|
29
|
+
codeFrame: '',
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const notFoundModule = match[1]
|
|
33
|
+
const importedFrom = match[2]
|
|
34
|
+
const rawLines = readFileSync(importedFrom, EncodingType.Utf8)
|
|
35
|
+
let line = 0
|
|
36
|
+
let column = 0
|
|
37
|
+
const splittedLines = SplitLines.splitLines(rawLines)
|
|
38
|
+
for (let i = 0; i < splittedLines.length; i++) {
|
|
39
|
+
const splittedLine = splittedLines[i]
|
|
40
|
+
const index = splittedLine.indexOf(notFoundModule)
|
|
41
|
+
if (index !== -1) {
|
|
42
|
+
line = i + 1
|
|
43
|
+
column = index
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const location = {
|
|
48
|
+
start: {
|
|
49
|
+
line,
|
|
50
|
+
column,
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
const codeFrame = codeFrameColumns(rawLines, location)
|
|
54
|
+
const stackLines = SplitLines.splitLines(error.stack)
|
|
55
|
+
const newStackLines = [stackLines[0], ` at ${importedFrom}:${line}:${column}`, ...stackLines.slice(1)]
|
|
56
|
+
const newStack = JoinLines.joinLines(newStackLines)
|
|
57
|
+
return {
|
|
58
|
+
message,
|
|
59
|
+
stack: newStack,
|
|
60
|
+
codeFrame,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getStackLinesToCut = (error) => {
|
|
65
|
+
if (error instanceof AssertionError) {
|
|
66
|
+
return 1
|
|
67
|
+
}
|
|
68
|
+
return 0
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const prepare = (error) => {
|
|
72
|
+
if (error && error.code === ErrorCodes.ERR_MODULE_NOT_FOUND) {
|
|
73
|
+
return prepareModuleNotFoundError(error)
|
|
74
|
+
}
|
|
75
|
+
const { message } = error
|
|
76
|
+
if (error && error.cause) {
|
|
77
|
+
const cause = error.cause()
|
|
78
|
+
if (cause) {
|
|
79
|
+
error = cause
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const linesToCut = getStackLinesToCut(error)
|
|
83
|
+
const lines = CleanStack.cleanStack(error.stack).slice(linesToCut)
|
|
84
|
+
const file = lines[0]
|
|
85
|
+
let codeFrame = ''
|
|
86
|
+
if (error.codeFrame) {
|
|
87
|
+
codeFrame = error.codeFrame
|
|
88
|
+
} else if (file) {
|
|
89
|
+
let match = file.match(/\((.*):(\d+):(\d+)\)$/)
|
|
90
|
+
if (!match) {
|
|
91
|
+
match = file.match(/at (.*):(\d+):(\d+)$/)
|
|
92
|
+
}
|
|
93
|
+
if (match) {
|
|
94
|
+
const [_, path, line, column] = match
|
|
95
|
+
const actualPath = getActualPath(path)
|
|
96
|
+
const rawLines = readFileSync(actualPath, EncodingType.Utf8)
|
|
97
|
+
const location = {
|
|
98
|
+
start: {
|
|
99
|
+
line: Number.parseInt(line),
|
|
100
|
+
column: Number.parseInt(column),
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
codeFrame = codeFrameColumns(rawLines, location)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const relevantStack = JoinLines.joinLines(lines)
|
|
107
|
+
return {
|
|
108
|
+
message,
|
|
109
|
+
stack: relevantStack,
|
|
110
|
+
codeFrame,
|
|
111
|
+
type: error.constructor.name,
|
|
112
|
+
code: error.code,
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const fixBackslashes = (string) => {
|
|
117
|
+
return string.replaceAll('\\\\', '\\')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const prepareJsonError = (json, property, message) => {
|
|
121
|
+
const string = fixBackslashes(Json.stringify(json))
|
|
122
|
+
const stringifiedPropertyName = `"${property}"`
|
|
123
|
+
const index = string.indexOf(stringifiedPropertyName) // TODO this could be wrong in some cases, find a better way
|
|
124
|
+
console.log({ string, index })
|
|
125
|
+
const jsonError = {
|
|
126
|
+
stack: '',
|
|
127
|
+
}
|
|
128
|
+
if (index !== -1) {
|
|
129
|
+
const lines = new LinesAndColumns(string)
|
|
130
|
+
const location = lines.locationForIndex(index + stringifiedPropertyName.length + 1)
|
|
131
|
+
const codeFrame = codeFrameColumns(string, {
|
|
132
|
+
// @ts-ignore
|
|
133
|
+
start: { line: location.line + 1, column: location.column + 1 },
|
|
134
|
+
})
|
|
135
|
+
jsonError.codeFrame = codeFrame
|
|
136
|
+
}
|
|
137
|
+
// jsonError.stack = `${bottomMessage}\n at ${filePath}`
|
|
138
|
+
return jsonError
|
|
139
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const argv = process.argv
|
|
2
|
+
|
|
3
|
+
export const env = process.env
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
* @param {number} code
|
|
8
|
+
*/
|
|
9
|
+
export const exit = (code) => {
|
|
10
|
+
process.exit(code)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {string} event
|
|
16
|
+
* @param {any} listener
|
|
17
|
+
*/
|
|
18
|
+
export const on = (event, listener) => {
|
|
19
|
+
process.on(event, listener)
|
|
20
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { join } from 'path'
|
|
2
|
+
import * as Assert from '../Assert/Assert.js'
|
|
3
|
+
import * as GetTests from '../GetTests/GetTests.js'
|
|
4
|
+
import * as RunTests from '../RunTests/RunTests.js'
|
|
5
|
+
import * as SetupTests from '../SetupTests/SetupTests.js'
|
|
6
|
+
import * as TearDownTests from '../TearDownTests/TearDownTests.js'
|
|
7
|
+
import * as JsonRpc from '../JsonRpc/JsonRpc.js'
|
|
8
|
+
import * as CliCommandType from '../CliCommandType/CliCommandType.js'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {any} ipc
|
|
12
|
+
* @param {string} extensionPath
|
|
13
|
+
* @param {string} testPath
|
|
14
|
+
* @param {string} cwd
|
|
15
|
+
* @param {boolean} headless
|
|
16
|
+
* @param {number} headless
|
|
17
|
+
*/
|
|
18
|
+
export const runAllTests = async (ipc, extensionPath, testPath, cwd, headless, timeout) => {
|
|
19
|
+
Assert.string(extensionPath)
|
|
20
|
+
Assert.string(testPath)
|
|
21
|
+
Assert.string(cwd)
|
|
22
|
+
Assert.boolean(headless)
|
|
23
|
+
Assert.number(timeout)
|
|
24
|
+
const controller = new AbortController()
|
|
25
|
+
const signal = controller.signal
|
|
26
|
+
const { browser, page, child, port } = await SetupTests.setupTests({
|
|
27
|
+
signal,
|
|
28
|
+
headless,
|
|
29
|
+
onlyExtension: extensionPath,
|
|
30
|
+
testPath,
|
|
31
|
+
})
|
|
32
|
+
const testSrc = join(testPath, 'src')
|
|
33
|
+
const tests = await GetTests.getTests(testSrc)
|
|
34
|
+
const onResult = (result) => {
|
|
35
|
+
JsonRpc.send(ipc, CliCommandType.HandleResult, result)
|
|
36
|
+
}
|
|
37
|
+
const onFinalResult = (finalResult) => {
|
|
38
|
+
JsonRpc.send(ipc, CliCommandType.HandleFinalResult, finalResult)
|
|
39
|
+
}
|
|
40
|
+
await RunTests.runTests({ testSrc, tests, headless, port, page, timeout, onResult, onFinalResult })
|
|
41
|
+
await TearDownTests.tearDownTests({
|
|
42
|
+
controller,
|
|
43
|
+
child,
|
|
44
|
+
})
|
|
45
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { expect } from '@playwright/test'
|
|
2
|
+
import { basename } from 'node:path'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import * as GetTestState from '../GetTestState/GetTestState.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} absolutePath
|
|
8
|
+
* @param {number} port
|
|
9
|
+
*/
|
|
10
|
+
const getUrlFromTestFile = (absolutePath, port) => {
|
|
11
|
+
const baseName = basename(absolutePath)
|
|
12
|
+
const htmlFileName = baseName.slice(0, -'.js'.length) + '.html'
|
|
13
|
+
return `http://localhost:${port}/tests/${htmlFileName}`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const runTest = async ({ test, page, testSrc, port, timeout }) => {
|
|
17
|
+
const start = performance.now()
|
|
18
|
+
const url = getUrlFromTestFile(test, port)
|
|
19
|
+
await page.goto(url, {
|
|
20
|
+
waitUntil: 'networkidle',
|
|
21
|
+
})
|
|
22
|
+
const testOverlay = page.locator('#TestOverlay')
|
|
23
|
+
await expect(testOverlay).toBeVisible({
|
|
24
|
+
timeout,
|
|
25
|
+
})
|
|
26
|
+
const text = await testOverlay.textContent()
|
|
27
|
+
const testOverlayState = await testOverlay.getAttribute('data-state')
|
|
28
|
+
const testState = GetTestState.getTestState(testOverlayState)
|
|
29
|
+
const end = performance.now()
|
|
30
|
+
return {
|
|
31
|
+
...testState,
|
|
32
|
+
name: test,
|
|
33
|
+
start,
|
|
34
|
+
end,
|
|
35
|
+
error: text,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as RunTest from '../RunTest/RunTest.js'
|
|
2
|
+
import * as TestState from '../TestState/TestState.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {{testSrc:string, tests:string[], headless:boolean, page: import('@playwright/test').Page, port:number, timeout:number, onResult:any, onFinalResult:any}} param0
|
|
7
|
+
*/
|
|
8
|
+
export const runTests = async ({ testSrc, tests, headless, page, port, timeout, onResult, onFinalResult }) => {
|
|
9
|
+
let failed = 0
|
|
10
|
+
let skipped = 0
|
|
11
|
+
let passed = 0
|
|
12
|
+
const start = performance.now()
|
|
13
|
+
for (const test of tests) {
|
|
14
|
+
const result = await RunTest.runTest({
|
|
15
|
+
test,
|
|
16
|
+
page,
|
|
17
|
+
testSrc,
|
|
18
|
+
port,
|
|
19
|
+
timeout,
|
|
20
|
+
})
|
|
21
|
+
onResult(result)
|
|
22
|
+
switch (result.status) {
|
|
23
|
+
case TestState.Fail:
|
|
24
|
+
failed++
|
|
25
|
+
break
|
|
26
|
+
case TestState.Skip:
|
|
27
|
+
skipped++
|
|
28
|
+
break
|
|
29
|
+
case TestState.Pass:
|
|
30
|
+
passed++
|
|
31
|
+
break
|
|
32
|
+
default:
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const end = performance.now()
|
|
37
|
+
onFinalResult({
|
|
38
|
+
passed,
|
|
39
|
+
failed,
|
|
40
|
+
skipped,
|
|
41
|
+
start,
|
|
42
|
+
end,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as StartBrowser from '../StartBrowser/StartBrowser.js'
|
|
2
|
+
import * as StartServer from '../StartServer/StartServer.js'
|
|
3
|
+
import * as GetPort from '../GetPort/GetPort.js'
|
|
4
|
+
import * as GetServerPath from '../GetServerPath/GetServerPath.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param {{signal:AbortSignal, headless: boolean, onlyExtension:string, testPath:string}} options
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
export const setupTests = async ({ signal, headless, onlyExtension, testPath }) => {
|
|
12
|
+
const port = await GetPort.getPort()
|
|
13
|
+
const { browser, page } = await StartBrowser.startBrowser({
|
|
14
|
+
signal,
|
|
15
|
+
headless,
|
|
16
|
+
})
|
|
17
|
+
const serverPath = await GetServerPath.getServerPath()
|
|
18
|
+
const child = await StartServer.startServer({ signal, port, serverPath, onlyExtension, testPath })
|
|
19
|
+
return {
|
|
20
|
+
port,
|
|
21
|
+
browser,
|
|
22
|
+
page,
|
|
23
|
+
child,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SIGINT = 'SIGINT'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { chromium } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {{signal:AbortSignal, headless:boolean}} options
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export const startBrowser = async ({ signal, headless }) => {
|
|
9
|
+
const browser = await chromium.launch({
|
|
10
|
+
headless,
|
|
11
|
+
})
|
|
12
|
+
const page = await browser.newPage()
|
|
13
|
+
signal.addEventListener('abort', async () => {
|
|
14
|
+
await page.close()
|
|
15
|
+
await browser.close()
|
|
16
|
+
})
|
|
17
|
+
return {
|
|
18
|
+
browser,
|
|
19
|
+
page,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { fork } from 'child_process'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {{signal:AbortSignal, port:number, serverPath:string, onlyExtension:string, testPath:string}} param0
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export const startServer = async ({ signal, port, serverPath, onlyExtension, testPath }) => {
|
|
9
|
+
const child = fork(serverPath, {
|
|
10
|
+
stdio: 'inherit',
|
|
11
|
+
// signal,
|
|
12
|
+
env: {
|
|
13
|
+
...process.env,
|
|
14
|
+
PORT: String(port),
|
|
15
|
+
ONLY_EXTENSION: onlyExtension,
|
|
16
|
+
TEST_PATH: testPath,
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
child.on('error', (x) => {
|
|
20
|
+
if (x.name === 'AbortError') {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
console.log('child error', x)
|
|
24
|
+
})
|
|
25
|
+
await new Promise((resolve) => {
|
|
26
|
+
child.on('message', resolve)
|
|
27
|
+
})
|
|
28
|
+
return child
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const RunAllTests = 'RunAllTests'
|