@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 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
+ [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](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'