@lvce-editor/test-with-playwright 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/README.md +21 -0
- package/package.json +43 -0
- package/src/all.js +54 -0
- package/src/main.d.ts +18 -0
- package/src/main.js +183 -0
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# @lvce-editor/test-with-playwright
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
import {
|
|
7
|
+
runWithExtension,
|
|
8
|
+
test,
|
|
9
|
+
expect,
|
|
10
|
+
} from '@lvce-editor/test-with-playwright'
|
|
11
|
+
|
|
12
|
+
test('sample.hello-world', async () => {
|
|
13
|
+
const page = await runWithExtension({})
|
|
14
|
+
const sideBar = page.locator('#SideBar')
|
|
15
|
+
await expect(sideBar).toBeVisible()
|
|
16
|
+
})
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Gitpod
|
|
20
|
+
|
|
21
|
+
[](https://gitpod.io/#https://github.com/lvce-editor/test-with-playwright)
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lvce-editor/test-with-playwright",
|
|
3
|
+
"version": "0.0.0-dev",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "src/main.js",
|
|
6
|
+
"types": "src/main.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"src"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"postinstall": "node ./scripts/postinstall.js",
|
|
13
|
+
"test": "node --unhandled-rejections=warn --experimental-vm-modules ./node_modules/jest/bin/jest.js --detectOpenHandles --forceExit"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git@github.com:lvce-editor/test-with-playwright.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [],
|
|
20
|
+
"author": "",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@playwright/test": "^1.23.0",
|
|
24
|
+
"get-port": "^6.1.2",
|
|
25
|
+
"read-pkg-up": "^9.1.0"
|
|
26
|
+
},
|
|
27
|
+
"prettier": {
|
|
28
|
+
"semi": false,
|
|
29
|
+
"singleQuote": true
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@lvce-editor/server": "^0.0.22",
|
|
33
|
+
"@types/jest": "^28.1.3",
|
|
34
|
+
"@types/node": "^18.0.0",
|
|
35
|
+
"execa": "^6.1.0",
|
|
36
|
+
"jest": "^28.1.2",
|
|
37
|
+
"prettier": "^2.7.1",
|
|
38
|
+
"typescript": "^4.7.4"
|
|
39
|
+
},
|
|
40
|
+
"jest": {
|
|
41
|
+
"testTimeout": 15000
|
|
42
|
+
}
|
|
43
|
+
}
|
package/src/all.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readdirSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { performance } from 'node:perf_hooks'
|
|
4
|
+
import { pathToFileURL } from 'node:url'
|
|
5
|
+
import { closeAll, getRoot, runTest, startAll, state } from './main.js'
|
|
6
|
+
|
|
7
|
+
const getTestFiles = async (root) => {
|
|
8
|
+
return readdirSync(root).map((x) => join(root, x))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const main = async () => {
|
|
12
|
+
try {
|
|
13
|
+
let skipped = 0
|
|
14
|
+
let passed = 0
|
|
15
|
+
const start = performance.now()
|
|
16
|
+
state.runImmediately = false
|
|
17
|
+
await startAll()
|
|
18
|
+
console.info('SETUP COMPLETE')
|
|
19
|
+
const root = await getRoot()
|
|
20
|
+
state.root = root
|
|
21
|
+
const testFiles = await getTestFiles(join(root, 'src'))
|
|
22
|
+
console.log({ testFiles })
|
|
23
|
+
for (const testFile of testFiles) {
|
|
24
|
+
state.tests = []
|
|
25
|
+
const testUri = pathToFileURL(testFile).toString()
|
|
26
|
+
await import(testUri)
|
|
27
|
+
for (const test of state.tests) {
|
|
28
|
+
if (test.status === 'skipped') {
|
|
29
|
+
skipped++
|
|
30
|
+
} else {
|
|
31
|
+
await runTest(test)
|
|
32
|
+
passed++
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const end = performance.now()
|
|
37
|
+
const duration = end - start
|
|
38
|
+
if (skipped) {
|
|
39
|
+
console.info(
|
|
40
|
+
`${passed} tests passed, ${skipped} tests skipped in ${duration}ms`
|
|
41
|
+
)
|
|
42
|
+
} else {
|
|
43
|
+
console.info(`${passed} tests passed in ${duration}ms`)
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.info('tests failed')
|
|
47
|
+
console.error(error)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
} finally {
|
|
50
|
+
await closeAll()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
main()
|
package/src/main.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Page } from '@playwright/test'
|
|
2
|
+
|
|
3
|
+
export const runWithExtension: ({
|
|
4
|
+
folder,
|
|
5
|
+
env,
|
|
6
|
+
}: {
|
|
7
|
+
folder?: string
|
|
8
|
+
env?: any
|
|
9
|
+
}) => Promise<Page>
|
|
10
|
+
|
|
11
|
+
export const test: {
|
|
12
|
+
(name: string, fn: () => Promise<void>): void
|
|
13
|
+
skip: (name: string, fn: () => Promise<void>) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { expect } from '@playwright/test'
|
|
17
|
+
|
|
18
|
+
export const getTmpDir: (prefix = 'foo-') => Promise<string>
|
package/src/main.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
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
|
+
tests: [],
|
|
33
|
+
port: 0,
|
|
34
|
+
root: undefined,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const getRoot = async () => {
|
|
38
|
+
const rootPackageJson = await readPackageUp()
|
|
39
|
+
if (!rootPackageJson) {
|
|
40
|
+
throw new Error('package json not found')
|
|
41
|
+
}
|
|
42
|
+
return dirname(rootPackageJson.path)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const getExtensionFolder = async () => {
|
|
46
|
+
const root = state.root || (await getRoot())
|
|
47
|
+
return join(root, '..', 'extension')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const getTmpDir = (prefix='foo-') => {
|
|
51
|
+
return mkdtemp(join(tmpdir(), prefix))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const launchServer = async ({ port, folder, env }) => {
|
|
55
|
+
const { serverPath } = await import('@lvce-editor/server')
|
|
56
|
+
// TODO disable saving state between tests in settings
|
|
57
|
+
const configDir = await getTmpDir()
|
|
58
|
+
await mkdir(join(configDir, 'lvce-oss'), { recursive: true })
|
|
59
|
+
await writeFile(
|
|
60
|
+
join(configDir, 'lvce-oss', 'settings.json'),
|
|
61
|
+
JSON.stringify(
|
|
62
|
+
{
|
|
63
|
+
'workbench.saveStateOnVisibilityChange': false,
|
|
64
|
+
},
|
|
65
|
+
null,
|
|
66
|
+
2
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
const extensionFolder = await getExtensionFolder()
|
|
70
|
+
const childProcess = fork(serverPath, {
|
|
71
|
+
stdio: 'inherit',
|
|
72
|
+
env: {
|
|
73
|
+
...process.env,
|
|
74
|
+
PORT: port,
|
|
75
|
+
FOLDER: folder,
|
|
76
|
+
ONLY_EXTENSION: extensionFolder,
|
|
77
|
+
XDG_CONFIG_HOME: configDir,
|
|
78
|
+
...env,
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
state.childProcess = childProcess
|
|
82
|
+
return new Promise((resolve, reject) => {
|
|
83
|
+
const handleMessage = (message) => {
|
|
84
|
+
if (message === 'ready') {
|
|
85
|
+
resolve(undefined)
|
|
86
|
+
} else {
|
|
87
|
+
reject(new Error('expected ready message'))
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
childProcess.once('message', handleMessage)
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const startBrowser = async ({ port, headless = false }) => {
|
|
95
|
+
assert(!state.browser, 'Browser should not be defined')
|
|
96
|
+
console.info('START BROWSER')
|
|
97
|
+
const browser = await chromium.launch({
|
|
98
|
+
headless,
|
|
99
|
+
})
|
|
100
|
+
state.browser = browser
|
|
101
|
+
const page = await browser.newPage({})
|
|
102
|
+
state.page = page
|
|
103
|
+
return page
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export const runWithExtension = async ({ folder = '', env = {} }) => {
|
|
107
|
+
folder ||= await getTmpDir()
|
|
108
|
+
if (state.page && state.childProcess) {
|
|
109
|
+
console.info('recycle page')
|
|
110
|
+
state.childProcess.send({
|
|
111
|
+
jsonrpc: '2.0',
|
|
112
|
+
method: 'Platform.setEnvironmentVariables',
|
|
113
|
+
params: [
|
|
114
|
+
{
|
|
115
|
+
FOLDER: folder,
|
|
116
|
+
...env,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
id: 999999999999,
|
|
120
|
+
})
|
|
121
|
+
await state.page.goto(`http://localhost:${state.port}`)
|
|
122
|
+
return state.page
|
|
123
|
+
}
|
|
124
|
+
const port = await getPort()
|
|
125
|
+
const server = await launchServer({ port, folder, env })
|
|
126
|
+
const page = await startBrowser({
|
|
127
|
+
headless: false,
|
|
128
|
+
port,
|
|
129
|
+
})
|
|
130
|
+
await page.goto(`http://localhost:${port}`)
|
|
131
|
+
return page
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const runTest = async ({ name, fn }) => {
|
|
135
|
+
const start = performance.now()
|
|
136
|
+
console.info(`[test] running ${name}`)
|
|
137
|
+
await fn()
|
|
138
|
+
const end = performance.now()
|
|
139
|
+
const duration = end - start
|
|
140
|
+
console.info(`[test] passed ${name} in ${duration}ms`)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const test = async (name, fn) => {
|
|
144
|
+
if (state.runImmediately) {
|
|
145
|
+
await runTest({ name, fn })
|
|
146
|
+
} else {
|
|
147
|
+
state.tests.push({ name, fn })
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
test.skip = (name, fn) => {
|
|
152
|
+
state.tests.push({ name, fn, status: 'skipped' })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const startAll = async () => {
|
|
156
|
+
const port = await getPort()
|
|
157
|
+
await launchServer({
|
|
158
|
+
port,
|
|
159
|
+
env: {},
|
|
160
|
+
folder: '',
|
|
161
|
+
})
|
|
162
|
+
state.port = port
|
|
163
|
+
const headless = process.argv.includes('--headless')
|
|
164
|
+
const page = await startBrowser({ port, headless })
|
|
165
|
+
return page
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const closeAll = async () => {
|
|
169
|
+
if (state.childProcess) {
|
|
170
|
+
state.childProcess.kill('SIGTERM')
|
|
171
|
+
state.childProcess = undefined
|
|
172
|
+
}
|
|
173
|
+
if (state.page) {
|
|
174
|
+
await state.page.close()
|
|
175
|
+
state.page = undefined
|
|
176
|
+
}
|
|
177
|
+
if (state.browser) {
|
|
178
|
+
await state.browser.close()
|
|
179
|
+
state.browser = undefined
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { expect } from '@playwright/test'
|