@lvce-editor/test-with-playwright 0.0.25 → 0.0.27
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/bin/test-with-playwright.js +1 -1
- package/package.json +8 -5
- package/src/parts/CloseAll/CloseAll.js +16 -0
- package/src/parts/ErrorCodes/ErrorCodes.js +2 -0
- package/src/parts/ExitCode/ExitCode.js +2 -0
- package/src/parts/GetPort/GetPort.js +3 -0
- package/src/parts/GetRoot/GetRoot.js +10 -0
- package/src/parts/GetTestFiles/GetTestFiles.js +21 -0
- package/src/parts/GetTmpDir/GetTmpDir.js +7 -0
- package/src/parts/InvariantError/InvariantError.js +0 -0
- package/src/parts/IsTestFile/IsTestFile.js +6 -0
- package/src/parts/LaunchServer/LaunchServer.js +51 -0
- package/src/parts/NoTestFilesFoundError/NoTestFIlesFoundError.js +8 -0
- package/src/parts/Process/Process.js +5 -0
- package/src/parts/RunAllTests/RunAllTests.js +56 -0
- package/src/parts/RunTest/RunTest.js +10 -0
- package/src/parts/RunTests/RunTests.js +83 -0
- package/src/parts/ShouldLogErrorWithStack/ShouldLogErrorWithStack.js +13 -0
- package/src/parts/StartAll/StartAll.js +18 -0
- package/src/parts/StartBrowser/StartBrowser.js +15 -0
- package/src/parts/TestOverlayTimeout/TestOverlayTimeout.js +1 -0
- package/src/parts/TestResultType/TestResultType.js +3 -0
- package/src/parts/TestState/TestState.js +23 -0
- package/src/all.js +0 -126
- package/src/main.js +0 -140
|
@@ -1 +1 @@
|
|
|
1
|
-
import '../src/
|
|
1
|
+
import '../src/parts/RunAllTests/RunAllTests.js'
|
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lvce-editor/test-with-playwright",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.27",
|
|
4
4
|
"description": "",
|
|
5
|
-
"main": "src/
|
|
6
|
-
"types": "src/main.d.ts",
|
|
5
|
+
"main": "src/parts/RunAllTests/RunAllTests.js",
|
|
7
6
|
"type": "module",
|
|
8
7
|
"bin": "bin/test-with-playwright.js",
|
|
9
8
|
"repository": {
|
|
@@ -14,9 +13,13 @@
|
|
|
14
13
|
"author": "",
|
|
15
14
|
"license": "MIT",
|
|
16
15
|
"dependencies": {
|
|
17
|
-
"@playwright/test": "^1.
|
|
16
|
+
"@playwright/test": "^1.34.3",
|
|
18
17
|
"get-port": "^6.1.2",
|
|
19
18
|
"minimist": "^1.2.8",
|
|
20
|
-
"read-pkg-up": "^9.1.0"
|
|
19
|
+
"read-pkg-up": "^9.1.0",
|
|
20
|
+
"verror": "^1.10.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/verror": "^1.10.6"
|
|
21
24
|
}
|
|
22
25
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as TestState from '../TestState/TestState.js'
|
|
2
|
+
|
|
3
|
+
export const closeAll = async () => {
|
|
4
|
+
if (TestState.state.childProcess) {
|
|
5
|
+
TestState.state.childProcess.kill('SIGTERM')
|
|
6
|
+
TestState.state.childProcess = undefined
|
|
7
|
+
}
|
|
8
|
+
if (TestState.state.page) {
|
|
9
|
+
await TestState.state.page.close()
|
|
10
|
+
TestState.state.page = undefined
|
|
11
|
+
}
|
|
12
|
+
if (TestState.state.browser) {
|
|
13
|
+
await TestState.state.browser.close()
|
|
14
|
+
TestState.state.browser = undefined
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { dirname } from 'node:path'
|
|
2
|
+
import { readPackageUp } from 'read-pkg-up'
|
|
3
|
+
|
|
4
|
+
export const getRoot = async () => {
|
|
5
|
+
const rootPackageJson = await readPackageUp()
|
|
6
|
+
if (!rootPackageJson) {
|
|
7
|
+
throw new Error('package json not found')
|
|
8
|
+
}
|
|
9
|
+
return dirname(rootPackageJson.path)
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
4
|
+
import * as IsTestFile from '../IsTestFile/IsTestFile.js'
|
|
5
|
+
import VError from 'verror'
|
|
6
|
+
import { NoTestFilesFoundError } from '../NoTestFilesFoundError/NoTestFIlesFoundError.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} root
|
|
10
|
+
*/
|
|
11
|
+
export const getTestFiles = async (root) => {
|
|
12
|
+
try {
|
|
13
|
+
const dirents = await readdir(root)
|
|
14
|
+
return dirents.filter(IsTestFile.isTestFile).map((x) => join(root, x))
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (error && error.code === ErrorCodes.ENOENT) {
|
|
17
|
+
throw new NoTestFilesFoundError(root)
|
|
18
|
+
}
|
|
19
|
+
throw new VError(error, `Failed to get test files`)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fork } from 'node:child_process'
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises'
|
|
3
|
+
import { join } from 'node:path'
|
|
4
|
+
import * as GetRoot from '../GetRoot/GetRoot.js'
|
|
5
|
+
import * as GetTmpDir from '../GetTmpDir/GetTmpDir.js'
|
|
6
|
+
import * as TestState from '../TestState/TestState.js'
|
|
7
|
+
|
|
8
|
+
const getExtensionFolder = async () => {
|
|
9
|
+
const root = TestState.state.root || (await GetRoot.getRoot())
|
|
10
|
+
return join(root, '..', 'extension')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const launchServer = async ({ port, folder, env }) => {
|
|
14
|
+
const { serverPath } = await import('@lvce-editor/server')
|
|
15
|
+
// TODO disable saving state between tests in settings
|
|
16
|
+
const configDir = await GetTmpDir.getTmpDir()
|
|
17
|
+
await mkdir(join(configDir, 'lvce-oss'), { recursive: true })
|
|
18
|
+
await writeFile(
|
|
19
|
+
join(configDir, 'lvce-oss', 'settings.json'),
|
|
20
|
+
JSON.stringify(
|
|
21
|
+
{
|
|
22
|
+
'workbench.saveStateOnVisibilityChange': false,
|
|
23
|
+
},
|
|
24
|
+
null,
|
|
25
|
+
2
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
const extensionFolder = await getExtensionFolder()
|
|
29
|
+
const childProcess = fork(serverPath, {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
env: {
|
|
32
|
+
...process.env,
|
|
33
|
+
PORT: port,
|
|
34
|
+
FOLDER: folder,
|
|
35
|
+
ONLY_EXTENSION: extensionFolder,
|
|
36
|
+
XDG_CONFIG_HOME: configDir,
|
|
37
|
+
...env,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
TestState.state.childProcess = childProcess
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const handleMessage = (message) => {
|
|
43
|
+
if (message === 'ready') {
|
|
44
|
+
resolve(undefined)
|
|
45
|
+
} else {
|
|
46
|
+
reject(new Error('expected ready message'))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
childProcess.once('message', handleMessage)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import parseArgv from 'minimist'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import * as CloseAll from '../CloseAll/CloseAll.js'
|
|
4
|
+
import * as ExitCode from '../ExitCode/ExitCode.js'
|
|
5
|
+
import * as GetRoot from '../GetRoot/GetRoot.js'
|
|
6
|
+
import * as GetTestFiles from '../GetTestFiles/GetTestFiles.js'
|
|
7
|
+
import * as Process from '../Process/Process.js'
|
|
8
|
+
import * as RunTests from '../RunTests/RunTests.js'
|
|
9
|
+
import * as ShouldLogErrorWithStack from '../ShouldLogErrorWithStack/ShouldLogErrorWithStack.js'
|
|
10
|
+
import * as StartAll from '../StartAll/StartAll.js'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {any} options
|
|
15
|
+
*/
|
|
16
|
+
const getEnv = (options) => {
|
|
17
|
+
const env = Object.create(null)
|
|
18
|
+
if (options['only-extension']) {
|
|
19
|
+
env['ONLY_EXTENSION'] = options['only-extension']
|
|
20
|
+
}
|
|
21
|
+
if (options['test-path']) {
|
|
22
|
+
env['TEST_PATH'] = options['test-path']
|
|
23
|
+
}
|
|
24
|
+
return env
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const main = async () => {
|
|
28
|
+
try {
|
|
29
|
+
const argv = Process.argv.slice(2)
|
|
30
|
+
const options = parseArgv(argv)
|
|
31
|
+
const env = getEnv(options)
|
|
32
|
+
const { page, port } = await StartAll.startAll(env)
|
|
33
|
+
const root = await GetRoot.getRoot()
|
|
34
|
+
const testFiles = await GetTestFiles.getTestFiles(join(root, 'src'))
|
|
35
|
+
console.log({ testFiles })
|
|
36
|
+
await RunTests.runTests({
|
|
37
|
+
testFiles,
|
|
38
|
+
options,
|
|
39
|
+
page,
|
|
40
|
+
port,
|
|
41
|
+
})
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.info('tests failed')
|
|
44
|
+
if (ShouldLogErrorWithStack.shouldLogErrorWithStack(error)) {
|
|
45
|
+
console.error(error)
|
|
46
|
+
} else {
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
console.error(error.message)
|
|
49
|
+
}
|
|
50
|
+
Process.exit(ExitCode.Error)
|
|
51
|
+
} finally {
|
|
52
|
+
await CloseAll.closeAll()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
main()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { performance } from 'node:perf_hooks'
|
|
2
|
+
|
|
3
|
+
export const runTest = async ({ name, fn }) => {
|
|
4
|
+
const start = performance.now()
|
|
5
|
+
console.info(`[test] running ${name}`)
|
|
6
|
+
await fn()
|
|
7
|
+
const end = performance.now()
|
|
8
|
+
const duration = end - start
|
|
9
|
+
console.info(`[test] passed ${name} in ${duration}ms`)
|
|
10
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { expect } from '@playwright/test'
|
|
2
|
+
import { basename } from 'node:path'
|
|
3
|
+
import { performance } from 'node:perf_hooks'
|
|
4
|
+
import * as TestOverlayTimeout from '../TestOverlayTimeout/TestOverlayTimeout.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
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {import('@playwright/test').Page} page
|
|
19
|
+
* @param {string} url
|
|
20
|
+
*/
|
|
21
|
+
const executeSingleTest = async (page, url) => {
|
|
22
|
+
await page.goto(url)
|
|
23
|
+
const testOverlay = page.locator('#TestOverlay')
|
|
24
|
+
await expect(testOverlay).toBeVisible({
|
|
25
|
+
timeout: TestOverlayTimeout.testOverlayTimeout,
|
|
26
|
+
})
|
|
27
|
+
const text = await testOverlay.textContent()
|
|
28
|
+
const state = await testOverlay.getAttribute('data-state')
|
|
29
|
+
switch (state) {
|
|
30
|
+
case 'pass':
|
|
31
|
+
return {
|
|
32
|
+
status: 'pass',
|
|
33
|
+
}
|
|
34
|
+
case 'skip':
|
|
35
|
+
return {
|
|
36
|
+
status: 'skip',
|
|
37
|
+
}
|
|
38
|
+
case 'fail':
|
|
39
|
+
return {
|
|
40
|
+
status: 'fail',
|
|
41
|
+
error: `${text}`,
|
|
42
|
+
}
|
|
43
|
+
default:
|
|
44
|
+
throw new Error(`unexpected test state: ${state}`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
* @param {{testFiles:any[], options:any, port:number, page: import('@playwright/test').Page}} param0
|
|
51
|
+
*/
|
|
52
|
+
export const runTests = async ({ testFiles, options, port, page }) => {
|
|
53
|
+
let skipped = 0
|
|
54
|
+
let passed = 0
|
|
55
|
+
const start = performance.now()
|
|
56
|
+
console.log({ testFiles })
|
|
57
|
+
for (const testFile of testFiles) {
|
|
58
|
+
const url = getUrlFromTestFile(testFile, port)
|
|
59
|
+
const name = basename(testFile)
|
|
60
|
+
const result = await executeSingleTest(page, url)
|
|
61
|
+
switch (result.status) {
|
|
62
|
+
case 'pass':
|
|
63
|
+
console.info(`test passed ${name}`)
|
|
64
|
+
passed++
|
|
65
|
+
break
|
|
66
|
+
case 'skip':
|
|
67
|
+
console.info(`test skipped ${name}`)
|
|
68
|
+
skipped++
|
|
69
|
+
break
|
|
70
|
+
case 'fail':
|
|
71
|
+
throw new Error(`Test Failed ${name}: ${result.error}`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const end = performance.now()
|
|
75
|
+
const duration = end - start
|
|
76
|
+
if (skipped) {
|
|
77
|
+
console.info(
|
|
78
|
+
`${passed} tests passed, ${skipped} tests skipped in ${duration}ms`
|
|
79
|
+
)
|
|
80
|
+
} else {
|
|
81
|
+
console.info(`${passed} tests passed in ${duration}ms`)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as ErrorCodes from '../ErrorCodes/ErrorCodes.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {any} error
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export const shouldLogErrorWithStack = (error) => {
|
|
9
|
+
if (error && error.code === ErrorCodes.E_NO_TEST_FILES) {
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as GetPort from '../GetPort/GetPort.js'
|
|
2
|
+
import * as LaunchServer from '../LaunchServer/LaunchServer.js'
|
|
3
|
+
import * as StartBrowser from '../StartBrowser/StartBrowser.js'
|
|
4
|
+
import * as TestState from '../TestState/TestState.js'
|
|
5
|
+
import * as Process from '../Process/Process.js'
|
|
6
|
+
|
|
7
|
+
export const startAll = async (env) => {
|
|
8
|
+
const port = await GetPort.getPort()
|
|
9
|
+
await LaunchServer.launchServer({
|
|
10
|
+
port,
|
|
11
|
+
env,
|
|
12
|
+
folder: '',
|
|
13
|
+
})
|
|
14
|
+
TestState.state.port = port
|
|
15
|
+
const headless = Process.argv.includes('--headless')
|
|
16
|
+
const page = await StartBrowser.startBrowser({ headless })
|
|
17
|
+
return { page, port }
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { chromium } from '@playwright/test'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import * as TestState from '../TestState/TestState.js'
|
|
4
|
+
|
|
5
|
+
export const startBrowser = async ({ headless = false }) => {
|
|
6
|
+
assert(!TestState.state.browser, 'Browser should not be defined')
|
|
7
|
+
console.info('START BROWSER')
|
|
8
|
+
const browser = await chromium.launch({
|
|
9
|
+
headless,
|
|
10
|
+
})
|
|
11
|
+
TestState.state.browser = browser
|
|
12
|
+
const page = await browser.newPage({})
|
|
13
|
+
TestState.state.page = page
|
|
14
|
+
return page
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const testOverlayTimeout = 25_000
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const state = {
|
|
2
|
+
/**
|
|
3
|
+
* @type {import('@playwright/test').Page|undefined}
|
|
4
|
+
*/
|
|
5
|
+
page: undefined,
|
|
6
|
+
/**
|
|
7
|
+
* @type {import('@playwright/test').Browser|undefined}
|
|
8
|
+
*/
|
|
9
|
+
browser: undefined,
|
|
10
|
+
/**
|
|
11
|
+
* @type {import('child_process').ChildProcess|undefined}
|
|
12
|
+
*/
|
|
13
|
+
childProcess: undefined,
|
|
14
|
+
/**
|
|
15
|
+
* @type{boolean}
|
|
16
|
+
*/
|
|
17
|
+
runImmediately: true,
|
|
18
|
+
/**
|
|
19
|
+
* @type{any}
|
|
20
|
+
*/
|
|
21
|
+
port: 0,
|
|
22
|
+
root: undefined,
|
|
23
|
+
}
|
package/src/all.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { expect } from '@playwright/test'
|
|
2
|
-
import { readdirSync } from 'node:fs'
|
|
3
|
-
import { basename, join } from 'node:path'
|
|
4
|
-
import { performance } from 'node:perf_hooks'
|
|
5
|
-
import { closeAll, getRoot, runTest, startAll, state } from './main.js'
|
|
6
|
-
import parseArgv from 'minimist'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {string} name
|
|
10
|
-
*/
|
|
11
|
-
const isTestFile = (name) => {
|
|
12
|
-
return !name.startsWith('_')
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {string} root
|
|
17
|
-
*/
|
|
18
|
-
const getTestFiles = async (root) => {
|
|
19
|
-
return readdirSync(root)
|
|
20
|
-
.filter(isTestFile)
|
|
21
|
-
.map((x) => join(root, x))
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param {string} absolutePath
|
|
26
|
-
* @param {number} port
|
|
27
|
-
*/
|
|
28
|
-
const getUrlFromTestFile = (absolutePath, port) => {
|
|
29
|
-
const baseName = basename(absolutePath)
|
|
30
|
-
const htmlFileName = baseName.slice(0, -'.js'.length) + '.html'
|
|
31
|
-
return `http://localhost:${port}/tests/${htmlFileName}`
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
* @param {import('@playwright/test').Page} page
|
|
37
|
-
* @param {string} url
|
|
38
|
-
*/
|
|
39
|
-
const executeSingleTest = async (page, url) => {
|
|
40
|
-
await page.goto(url)
|
|
41
|
-
const testOverlay = page.locator('#TestOverlay')
|
|
42
|
-
await expect(testOverlay).toBeVisible({ timeout: 25_000 })
|
|
43
|
-
const text = await testOverlay.textContent()
|
|
44
|
-
const state = await testOverlay.getAttribute('data-state')
|
|
45
|
-
switch (state) {
|
|
46
|
-
case 'pass':
|
|
47
|
-
return {
|
|
48
|
-
status: 'pass',
|
|
49
|
-
}
|
|
50
|
-
case 'skip':
|
|
51
|
-
return {
|
|
52
|
-
status: 'skip',
|
|
53
|
-
}
|
|
54
|
-
case 'fail':
|
|
55
|
-
return {
|
|
56
|
-
status: 'fail',
|
|
57
|
-
error: `${text}`,
|
|
58
|
-
}
|
|
59
|
-
default:
|
|
60
|
-
throw new Error(`unexpected test state: ${state}`)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
66
|
-
* @param {any} options
|
|
67
|
-
*/
|
|
68
|
-
const getEnv = (options) => {
|
|
69
|
-
const env = Object.create(null)
|
|
70
|
-
if (options['only-extension']) {
|
|
71
|
-
env['ONLY_EXTENSION'] = options['only-extension']
|
|
72
|
-
}
|
|
73
|
-
if (options['test-path']) {
|
|
74
|
-
env['TEST_PATH'] = options['test-path']
|
|
75
|
-
}
|
|
76
|
-
return env
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const main = async () => {
|
|
80
|
-
try {
|
|
81
|
-
const argv = process.argv.slice(2)
|
|
82
|
-
const options = parseArgv(argv)
|
|
83
|
-
let skipped = 0
|
|
84
|
-
let passed = 0
|
|
85
|
-
const env = getEnv(options)
|
|
86
|
-
const start = performance.now()
|
|
87
|
-
const { page, port } = await startAll(env)
|
|
88
|
-
const root = await getRoot()
|
|
89
|
-
const testFiles = await getTestFiles(join(root, 'src'))
|
|
90
|
-
console.log({ testFiles })
|
|
91
|
-
for (const testFile of testFiles) {
|
|
92
|
-
const url = getUrlFromTestFile(testFile, port)
|
|
93
|
-
const name = basename(testFile)
|
|
94
|
-
const result = await executeSingleTest(page, url)
|
|
95
|
-
switch (result.status) {
|
|
96
|
-
case 'pass':
|
|
97
|
-
console.info(`test passed ${name}`)
|
|
98
|
-
passed++
|
|
99
|
-
break
|
|
100
|
-
case 'skip':
|
|
101
|
-
console.info(`test skipped ${name}`)
|
|
102
|
-
skipped++
|
|
103
|
-
break
|
|
104
|
-
case 'fail':
|
|
105
|
-
throw new Error(`Test Failed ${name}: ${result.error}`)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const end = performance.now()
|
|
109
|
-
const duration = end - start
|
|
110
|
-
if (skipped) {
|
|
111
|
-
console.info(
|
|
112
|
-
`${passed} tests passed, ${skipped} tests skipped in ${duration}ms`
|
|
113
|
-
)
|
|
114
|
-
} else {
|
|
115
|
-
console.info(`${passed} tests passed in ${duration}ms`)
|
|
116
|
-
}
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.info('tests failed')
|
|
119
|
-
console.error(error)
|
|
120
|
-
process.exit(1)
|
|
121
|
-
} finally {
|
|
122
|
-
await closeAll()
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
main()
|
package/src/main.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { chromium } from '@playwright/test'
|
|
2
|
-
import assert from 'assert'
|
|
3
|
-
import { fork } from 'child_process'
|
|
4
|
-
import { mkdir, mkdtemp, writeFile } from 'fs/promises'
|
|
5
|
-
import getPort from 'get-port'
|
|
6
|
-
import { join } from 'node:path'
|
|
7
|
-
import { performance } from 'node:perf_hooks'
|
|
8
|
-
import { tmpdir } from 'os'
|
|
9
|
-
import { dirname } from 'path'
|
|
10
|
-
import { readPackageUp } from 'read-pkg-up'
|
|
11
|
-
|
|
12
|
-
export const state = {
|
|
13
|
-
/**
|
|
14
|
-
* @type {import('@playwright/test').Page|undefined}
|
|
15
|
-
*/
|
|
16
|
-
page: undefined,
|
|
17
|
-
/**
|
|
18
|
-
* @type {import('@playwright/test').Browser|undefined}
|
|
19
|
-
*/
|
|
20
|
-
browser: undefined,
|
|
21
|
-
/**
|
|
22
|
-
* @type {import('child_process').ChildProcess|undefined}
|
|
23
|
-
*/
|
|
24
|
-
childProcess: undefined,
|
|
25
|
-
/**
|
|
26
|
-
* @type{boolean}
|
|
27
|
-
*/
|
|
28
|
-
runImmediately: true,
|
|
29
|
-
/**
|
|
30
|
-
* @type{any}
|
|
31
|
-
*/
|
|
32
|
-
port: 0,
|
|
33
|
-
root: undefined,
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const getRoot = async () => {
|
|
37
|
-
const rootPackageJson = await readPackageUp()
|
|
38
|
-
if (!rootPackageJson) {
|
|
39
|
-
throw new Error('package json not found')
|
|
40
|
-
}
|
|
41
|
-
return dirname(rootPackageJson.path)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const getExtensionFolder = async () => {
|
|
45
|
-
const root = state.root || (await getRoot())
|
|
46
|
-
return join(root, '..', 'extension')
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export const getTmpDir = (prefix = 'foo-') => {
|
|
50
|
-
return mkdtemp(join(tmpdir(), prefix))
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const launchServer = async ({ port, folder, env }) => {
|
|
54
|
-
const { serverPath } = await import('@lvce-editor/server')
|
|
55
|
-
// TODO disable saving state between tests in settings
|
|
56
|
-
const configDir = await getTmpDir()
|
|
57
|
-
await mkdir(join(configDir, 'lvce-oss'), { recursive: true })
|
|
58
|
-
await writeFile(
|
|
59
|
-
join(configDir, 'lvce-oss', 'settings.json'),
|
|
60
|
-
JSON.stringify(
|
|
61
|
-
{
|
|
62
|
-
'workbench.saveStateOnVisibilityChange': false,
|
|
63
|
-
},
|
|
64
|
-
null,
|
|
65
|
-
2
|
|
66
|
-
)
|
|
67
|
-
)
|
|
68
|
-
const extensionFolder = await getExtensionFolder()
|
|
69
|
-
const childProcess = fork(serverPath, {
|
|
70
|
-
stdio: 'inherit',
|
|
71
|
-
env: {
|
|
72
|
-
...process.env,
|
|
73
|
-
PORT: port,
|
|
74
|
-
FOLDER: folder,
|
|
75
|
-
ONLY_EXTENSION: extensionFolder,
|
|
76
|
-
XDG_CONFIG_HOME: configDir,
|
|
77
|
-
...env,
|
|
78
|
-
},
|
|
79
|
-
})
|
|
80
|
-
state.childProcess = childProcess
|
|
81
|
-
return new Promise((resolve, reject) => {
|
|
82
|
-
const handleMessage = (message) => {
|
|
83
|
-
if (message === 'ready') {
|
|
84
|
-
resolve(undefined)
|
|
85
|
-
} else {
|
|
86
|
-
reject(new Error('expected ready message'))
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
childProcess.once('message', handleMessage)
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
const startBrowser = async ({ port, headless = false }) => {
|
|
94
|
-
assert(!state.browser, 'Browser should not be defined')
|
|
95
|
-
console.info('START BROWSER')
|
|
96
|
-
const browser = await chromium.launch({
|
|
97
|
-
headless,
|
|
98
|
-
})
|
|
99
|
-
state.browser = browser
|
|
100
|
-
const page = await browser.newPage({})
|
|
101
|
-
state.page = page
|
|
102
|
-
return page
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export const runTest = async ({ name, fn }) => {
|
|
106
|
-
const start = performance.now()
|
|
107
|
-
console.info(`[test] running ${name}`)
|
|
108
|
-
await fn()
|
|
109
|
-
const end = performance.now()
|
|
110
|
-
const duration = end - start
|
|
111
|
-
console.info(`[test] passed ${name} in ${duration}ms`)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export const startAll = async (env) => {
|
|
115
|
-
const port = await getPort()
|
|
116
|
-
await launchServer({
|
|
117
|
-
port,
|
|
118
|
-
env,
|
|
119
|
-
folder: '',
|
|
120
|
-
})
|
|
121
|
-
state.port = port
|
|
122
|
-
const headless = process.argv.includes('--headless')
|
|
123
|
-
const page = await startBrowser({ port, headless })
|
|
124
|
-
return { page, port }
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export const closeAll = async () => {
|
|
128
|
-
if (state.childProcess) {
|
|
129
|
-
state.childProcess.kill('SIGTERM')
|
|
130
|
-
state.childProcess = undefined
|
|
131
|
-
}
|
|
132
|
-
if (state.page) {
|
|
133
|
-
await state.page.close()
|
|
134
|
-
state.page = undefined
|
|
135
|
-
}
|
|
136
|
-
if (state.browser) {
|
|
137
|
-
await state.browser.close()
|
|
138
|
-
state.browser = undefined
|
|
139
|
-
}
|
|
140
|
-
}
|