Skip to content

Commit 46b83cd

Browse files
committed
feat: add e2e test
1 parent defcf39 commit 46b83cd

File tree

5 files changed

+816
-26
lines changed

5 files changed

+816
-26
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,5 @@ dist
106106
.DS_Store
107107
# build
108108
index.cjs
109+
# e2e test
110+
electron-vite-test

__tests__/e2e.spec.ts

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
import cp from 'node:child_process'
4+
import {
5+
type ElectronApplication,
6+
type Page,
7+
type JSHandle,
8+
_electron as electron,
9+
} from 'playwright'
10+
import type { BrowserWindow } from 'electron'
11+
import {
12+
beforeAll,
13+
afterAll,
14+
describe,
15+
expect,
16+
test,
17+
} from 'vitest'
18+
19+
const CLI_PATH = path.join(__dirname, '..')
20+
const projectName = 'electron-vite-test'
21+
const generatePath = path.join(CLI_PATH, projectName)
22+
let electronApp: ElectronApplication
23+
let page: Page
24+
25+
beforeAll(async () => {
26+
fs.rmSync(generatePath, { recursive: true, force: true })
27+
28+
await createProject()
29+
30+
// enableElectronMirror()
31+
32+
const installLogs = execSync('npm install')
33+
writeFileSync('npm-install.log', installLogs)
34+
35+
const buildLogs = execSync('vite build')
36+
writeFileSync('vite-build.log', buildLogs)
37+
38+
electronApp = await electron.launch({
39+
args: ['.', '--no-sandbox'],
40+
cwd: generatePath,
41+
env: { ...process.env, NODE_ENV: 'development' },
42+
})
43+
page = await electronApp.firstWindow()
44+
45+
const mainWin: JSHandle<BrowserWindow> = await electronApp.browserWindow(page)
46+
await mainWin.evaluate(async (win) => {
47+
win.webContents.executeJavaScript('console.log("Execute JavaScript with e2e testing.")')
48+
})
49+
}, 1000 * 60 * 3)
50+
51+
afterAll(async () => {
52+
await page.close()
53+
await electronApp.close()
54+
})
55+
56+
describe('[create-electron-vite] e2e tests', async () => {
57+
test('startup', async () => {
58+
const title = await page.title()
59+
expect(title).eq('Vite + Vue + TS')
60+
})
61+
62+
test('should be home page is load correctly', async () => {
63+
const h1 = await page.$('h1')
64+
const title = await h1?.textContent()
65+
expect(title).eq('Vite + Vue')
66+
})
67+
68+
test('should be count button can click', async () => {
69+
const countButton = await page.$('button')
70+
await countButton?.click()
71+
const countValue = await countButton?.textContent()
72+
expect(countValue).eq('count is 1')
73+
})
74+
})
75+
76+
async function createProject() {
77+
return new Promise((resolve) => {
78+
const child = cp.spawn('node', [CLI_PATH, projectName])
79+
80+
child.stdout.on('data', (chunk) => {
81+
const stdout: string = chunk.toString()
82+
83+
if (stdout.includes('Project template:')) {
84+
child.stdin.write('\n')
85+
} else if (stdout.includes('Done. Now run:')) {
86+
child.kill()
87+
resolve(projectName)
88+
}
89+
})
90+
})
91+
}
92+
93+
// For local testing
94+
function enableElectronMirror() {
95+
const npmrc = path.join(generatePath, '.npmrc')
96+
let npmrcContent = fs.readFileSync(npmrc, 'utf8')
97+
98+
npmrcContent = npmrcContent
99+
.split('\n')
100+
.map((line) => line.includes('electron_mirror') ? line.replace('#', '').trim() : line)
101+
.join('\n')
102+
103+
fs.writeFileSync(npmrc, npmrcContent)
104+
}
105+
106+
function execSync(command: string) {
107+
return cp.execSync(command, { cwd: generatePath, encoding: 'utf8' })
108+
}
109+
110+
function writeFileSync(file: string, content: string) {
111+
return fs.writeFileSync(path.join(generatePath, file), content)
112+
}
113+
114+
function intervalTask<R>(fn: (args: { stop: () => void }) => R | Promise<R>, options?: {
115+
delay?: number
116+
timeout?: number
117+
}) {
118+
const {
119+
delay = 99,
120+
timeout = 1000 * 60 * 1,
121+
} = options ?? {}
122+
const startTime = Date.now()
123+
let done = false
124+
125+
return new Promise<R>((resolve, reject) => {
126+
const run = async () => {
127+
if (Date.now() - startTime > timeout) {
128+
reject('Interval task timeout')
129+
return
130+
}
131+
132+
const result = await fn({
133+
stop() {
134+
done = true
135+
},
136+
})
137+
if (done) {
138+
resolve(result)
139+
} else {
140+
setTimeout(run, delay)
141+
}
142+
}
143+
run()
144+
})
145+
}
146+
147+
function getDom(selector: string) {
148+
return intervalTask(async (args) => {
149+
const dom = await page.$(selector)
150+
if (dom) {
151+
args.stop()
152+
return dom
153+
}
154+
})
155+
}

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@
4747
"devDependencies": {
4848
"@types/node": "^22.1.0",
4949
"@types/prompts": "^2.4.9",
50+
"electron": "^33.0.2",
5051
"execa": "^9.3.0",
52+
"playwright": "^1.48.2",
5153
"pnpm": "9.7.0",
5254
"typescript": "^5.5.4",
5355
"vite": "^5.4.0",

0 commit comments

Comments
 (0)