convert jest tests to child_process run script (#2308)

* convert jest tests to child_process run script

* remove vuecli (it freezes), add angular

* run cargo build in correct dir

* add in asserts on fs

* normalize assert for node14

* fix installing empty dependencies

* add messages to asserts

* use test dir to check if running local cli

* try running in parallel

* run in parallel and log output serially

* avoid parallel, skip yarn for now

* add change file

* disable running with npm6 due to vite fail

Co-authored-by: amrbashir <48618675+amrbashir@users.noreply.github.com>
This commit is contained in:
Jacob Bolda 2021-08-08 03:13:05 -05:00 committed by GitHub
parent 63ad303903
commit c410e034f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1049 additions and 2703 deletions

View File

@ -0,0 +1,6 @@
---
"create-tauri-app": patch
"cli.js": patch
---
Adjust check for `dev` mode and switch CTA test to a script runner. The script gives us more control and better output into any failures.

View File

@ -3,6 +3,9 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
name: test create-tauri-app name: test create-tauri-app
env:
RUST_BACKTRACE: 1
TAURI_RECIPE: 'vanillajs,cra,vite,ngcli'
on: on:
workflow_dispatch: workflow_dispatch:
@ -13,20 +16,16 @@ on:
paths: paths:
- "tooling/create-tauri-app/**" - "tooling/create-tauri-app/**"
env:
RUST_BACKTRACE: 1
jobs: jobs:
create-recipe-with-npm: create-recipe-with-npm:
name: "node@${{ matrix.node }} + npm@${{ matrix.manager }}: ${{ matrix.recipe }}" name: "node@${{ matrix.node }} + npm@${{ matrix.manager }}"
runs-on: ${{ github.event.inputs.platform || 'ubuntu' }}-latest runs-on: ${{ github.event.inputs.platform || 'ubuntu' }}-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node: ["14", "16"] node: ["14", "16"]
manager: ["6", "7"] manager: ["7"]
recipe: ["vanillajs", "cra", "vite", "ngcli", "svelte", "dominator"]
exclude: exclude:
- node: "16" - node: "16"
manager: "6" manager: "6"
@ -62,40 +61,37 @@ jobs:
- run: yarn test - run: yarn test
working-directory: tooling/create-tauri-app working-directory: tooling/create-tauri-app
env: env:
TAURI_RECIPE: ${{ matrix.recipe }}
TAURI_RUN_MANAGER: "npm" TAURI_RUN_MANAGER: "npm"
create-recipe-with-yarn: # create-recipe-with-yarn:
name: "node@${{ matrix.node }} + yarn@1: ${{ matrix.recipe }}" # name: "node@${{ matrix.node }} + yarn@1"
runs-on: ${{ github.event.inputs.platform || 'ubuntu' }}-latest # runs-on: ${{ github.event.inputs.platform || 'ubuntu' }}-latest
strategy: # strategy:
fail-fast: false # fail-fast: false
matrix: # matrix:
node: ["14", "16"] # node: ["14", "16"]
recipe: ["vanillajs", "cra", "vite", "ngcli", "svelte"]
steps: # steps:
- uses: actions/checkout@v2 # - uses: actions/checkout@v2
- name: install stable # - name: install stable
uses: actions-rs/toolchain@v1 # uses: actions-rs/toolchain@v1
with: # with:
toolchain: stable # toolchain: stable
- uses: volta-cli/action@v1 # - uses: volta-cli/action@v1
with: # with:
node-version: ${{ matrix.node }} # node-version: ${{ matrix.node }}
yarn-version: 1.22.5 # yarn-version: 1.22.5
- name: install webkit2gtk # - name: install webkit2gtk
if: (github.event.inputs.platform || 'ubuntu') == 'ubuntu' # if: (github.event.inputs.platform || 'ubuntu') == 'ubuntu'
run: | # run: |
sudo apt-get update # sudo apt-get update
sudo apt-get install -y libgtk-3-dev libgtksourceview-3.0-dev webkit2gtk-4.0 libappindicator3-dev # sudo apt-get install -y libgtk-3-dev libgtksourceview-3.0-dev webkit2gtk-4.0 libappindicator3-dev
- run: yarn # - run: yarn
working-directory: tooling/create-tauri-app # working-directory: tooling/create-tauri-app
- run: yarn build # - run: yarn build
working-directory: tooling/create-tauri-app # working-directory: tooling/create-tauri-app
- run: yarn test # - run: yarn test
working-directory: tooling/create-tauri-app # working-directory: tooling/create-tauri-app
env: # env:
TAURI_RECIPE: ${{ matrix.recipe }} # TAURI_RUN_MANAGER: "yarn"
TAURI_RUN_MANAGER: "yarn"

View File

@ -57,8 +57,8 @@ export async function runOnRustCli(
onClose onClose
) )
} else { } else {
if (existsSync(resolve(targetPath, '../bundler'))) { if (existsSync(resolve(targetPath, 'test'))) {
// running local CLI // running local CLI since test directory exists
const cliPath = resolve(targetPath, '../cli.rs') const cliPath = resolve(targetPath, '../cli.rs')
spawnSync('cargo', ['build', '--release'], cliPath) spawnSync('cargo', ['build', '--release'], cliPath)
const localCliPath = resolve( const localCliPath = resolve(

View File

@ -29,7 +29,7 @@
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn", "lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
"format": "prettier --write --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore", "format": "prettier --write --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
"format:check": "prettier --check --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore", "format:check": "prettier --check --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
"test": "jest --runInBand" "test": "node ./test/spawn.test.mjs"
}, },
"dependencies": { "dependencies": {
"chalk": "4.1.1", "chalk": "4.1.1",
@ -39,16 +39,17 @@
"scaffe": "1.1.0" "scaffe": "1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@effection/process": "2.0.0-beta.8",
"@rollup/plugin-commonjs": "19.0.0", "@rollup/plugin-commonjs": "19.0.0",
"@rollup/plugin-node-resolve": "13.0.0", "@rollup/plugin-node-resolve": "13.0.0",
"@rollup/plugin-typescript": "8.2.1", "@rollup/plugin-typescript": "8.2.1",
"@types/cross-spawn": "6.0.2", "@types/cross-spawn": "6.0.2",
"@types/inquirer": "7.3.1", "@types/inquirer": "7.3.1",
"@types/jest": "26.0.23",
"@types/minimist": "1.2.1", "@types/minimist": "1.2.1",
"@types/semver": "7.3.6", "@types/semver": "7.3.6",
"@typescript-eslint/eslint-plugin": "4.25.0", "@typescript-eslint/eslint-plugin": "4.25.0",
"@typescript-eslint/parser": "4.25.0", "@typescript-eslint/parser": "4.25.0",
"effection": "2.0.0-beta.8",
"eslint": "7.27.0", "eslint": "7.27.0",
"eslint-config-prettier": "8.3.0", "eslint-config-prettier": "8.3.0",
"eslint-config-standard-with-typescript": "20.0.0", "eslint-config-standard-with-typescript": "20.0.0",
@ -57,11 +58,9 @@
"eslint-plugin-node": "11.1.0", "eslint-plugin-node": "11.1.0",
"eslint-plugin-promise": "5.1.0", "eslint-plugin-promise": "5.1.0",
"eslint-plugin-security": "1.4.0", "eslint-plugin-security": "1.4.0",
"fixturez": "1.1.0",
"jest": "27.0.3",
"prettier": "2.3.0", "prettier": "2.3.0",
"rollup": "2.50.4", "rollup": "2.50.4",
"ts-jest": "27.0.1", "temp-dir": "^2.0.0",
"tslib": "2.2.0", "tslib": "2.2.0",
"typescript": "4.3.2" "typescript": "4.3.2"
} }

View File

@ -368,7 +368,7 @@ const runInit = async (argv: Argv): Promise<void> => {
logStep(`Updating ${reset(yellow('"package.json"'))}`) logStep(`Updating ${reset(yellow('"package.json"'))}`)
updatePackageJson(appDirectory, appName) updatePackageJson(appDirectory, appName)
logStep(`Running: ${reset(yellow('tauri init'))}`) logStep(`Running ${reset(yellow('"tauri init"'))}`)
const binary = !argv.b ? packageManager : resolve(appDirectory, argv.b) const binary = !argv.b ? packageManager : resolve(appDirectory, argv.b)
// pnpm is equivalent to yarn and can run srcipts without using "run" but due to this bug https://github.com/pnpm/pnpm/issues/2764 // pnpm is equivalent to yarn and can run srcipts without using "run" but due to this bug https://github.com/pnpm/pnpm/issues/2764
// we need to pass "--" to pnpm or arguments won't be parsed correctly so for this command only we are gonna treat pnpm as npm equivalent/ // we need to pass "--" to pnpm or arguments won't be parsed correctly so for this command only we are gonna treat pnpm as npm equivalent/

View File

@ -1,169 +0,0 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import execa from 'execa'
import fixtures from 'fixturez'
const f = fixtures(__dirname)
import path from 'path'
import fs from 'fs'
const ctaBinary = path.resolve('./bin/create-tauri-app.js')
const clijs = path.resolve('../cli.js/')
const api = path.resolve('../api/')
const manager = process.env.TAURI_RUN_MANAGER ?? 'npm'
const recipes = process.env.TAURI_RECIPE
? [process.env.TAURI_RECIPE]
: ['vanillajs', 'cra', 'vite', 'vuecli', 'ngcli', 'svelte', 'dominator']
const timeoutLong = 900000
const timeoutLittleLonger = 930000
const logOut = false ? 'inherit' : 'pipe'
describe('CTA', () => {
console.warn(
'NOTE: You need to have installed and built cli.js and api before running the tests.'
)
describe.each(recipes.map((recipe) => [recipe, 'tauri-app']))(
`%s recipe`,
(recipe: string, appName: string) => {
it(
'runs',
async () => {
// creates a temp folder to run CTA within (this is our cwd)
const folder = f.temp()
const appFolder = path.join(folder, appName)
// runs CTA with all args set to avoid any prompts
const cta = await execa(
'node',
[
ctaBinary,
'--manager',
manager,
'--recipe',
recipe,
'--ci',
'--dev'
],
{
all: true,
stdio: logOut,
cwd: folder,
timeout: timeoutLong
}
)
// check to make certain it didn't fail anywhere
expect(cta.failed).toBe(false)
expect(cta.timedOut).toBe(false)
expect(cta.isCanceled).toBe(false)
expect(cta.killed).toBe(false)
expect(cta.signal).toBe(undefined)
const packageFileInitial: {
[k: string]: string | object
} = JSON.parse(
await fs.promises.readFile(
path.join(appFolder, 'package.json'),
'utf-8'
)
)
expect(packageFileInitial['name']).toBe(appName)
// run a tauri build to check if what we produced
// can actually create an app
// TODO long term we will want to hook this up to a real test harness
// and then run that test suite instead
let opts: string[] = []
if (manager === 'npm') {
opts =
recipe == 'vuecli'
? ['run', 'tauri:build']
: ['run', 'tauri', '--', 'build']
} else if (manager === 'yarn') {
opts = recipe == 'vuecli' ? ['tauri:build'] : ['tauri', 'build']
}
const tauriBuild = await execa(manager, opts, {
all: true,
stdio: logOut,
cwd: appFolder,
timeout: timeoutLong
})
expect(tauriBuild.failed).toBe(false)
expect(tauriBuild.timedOut).toBe(false)
expect(tauriBuild.isCanceled).toBe(false)
expect(tauriBuild.killed).toBe(false)
expect(tauriBuild.signal).toBe(undefined)
const packageFileOutput: {
[k: string]: string | object
} = JSON.parse(
await fs.promises.readFile(
path.join(appFolder, 'package.json'),
'utf-8'
)
)
expect(packageFileOutput['name']).toBe(appName)
const assertCustom: { [k: string]: Function } = {
vanillajs: () => {
expect(packageFileOutput['scripts']).toMatchObject({
tauri: 'tauri'
})
},
cra: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
tauri: 'tauri'
})
)
},
vite: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
tauri: 'tauri'
})
)
},
vuecli: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
'tauri:build': expect.anything(),
'tauri:serve': expect.anything()
})
)
},
ngcli: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
ng: 'ng',
tauri: 'tauri'
})
)
},
svelte: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
tauri: 'tauri'
})
)
},
dominator: () => {
expect(packageFileOutput['scripts']).toEqual(
expect.objectContaining({
tauri: 'tauri'
})
)
}
}
const getCustomAsserts = assertCustom[recipe]
if (getCustomAsserts) getCustomAsserts()
},
timeoutLittleLonger
)
}
)
})

View File

@ -0,0 +1,194 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { main, spawn, sleep } from 'effection'
import { exec } from '@effection/process'
import fs from 'fs/promises'
import path from 'path'
import crypto from 'crypto'
import tempDirectory from 'temp-dir'
let assert
const nodeVersion = process.versions.node.split('.')[0]
if (nodeVersion === '14') {
assert = await import('assert')
} else {
assert = await import('assert/strict')
}
const ctaBinary = path.resolve('./bin/create-tauri-app.js')
const clijs = path.resolve('../cli.js/')
const api = path.resolve('../api/')
const manager = process.env.TAURI_RUN_MANAGER || 'yarn'
const recipes = process.env.TAURI_RECIPE
? process.env.TAURI_RECIPE.split(',')
: ['vanillajs', 'cra', 'vite', 'ngcli']
const parallelize = process.env.TAURI_RECIPE_PARALLELIZE || false
main(function* start() {
const tauriTemp = path.join(
tempDirectory,
`tauri_${crypto.randomBytes(16).toString('hex')}`
)
try {
const appName = 'tauri-app'
let output = {}
for (let i = 0; i < recipes.length; i++) {
const recipe = recipes[i]
console.log(`::group::recipe ${recipe}`)
console.log(`------------------ ${recipe} started -------------------`)
const recipeFolder = path.join(tauriTemp, recipe)
const appFolder = path.join(recipeFolder, appName)
yield fs.mkdir(recipeFolder, { recursive: true })
console.log(`${recipeFolder} created.`)
// runs CTA with all args set to avoid any prompts
const run = `node '${ctaBinary}' --manager ${manager} --recipe ${recipe} --ci --dev`
console.log(`[running] ${run}`)
let opts = []
if (manager === 'npm') {
opts =
recipe == 'vuecli'
? ['run', 'tauri:build']
: ['run', 'tauri', '--', 'build']
} else if (manager === 'yarn') {
opts = recipe == 'vuecli' ? ['tauri:build'] : ['tauri', 'build']
}
if (!parallelize) {
const cta = yield exec(run, { cwd: recipeFolder })
yield streamLogs(cta)
// now it is finished, assert on some things
yield assertCTAState({ appFolder, appName })
const tauriBuild = yield exec(manager, {
arguments: opts,
cwd: appFolder
})
yield streamLogs(tauriBuild)
// build is complete, assert on some things
yield assertTauriBuildState({ appFolder, appName })
} else {
console.log('running CTA recipe in parallel')
output[recipe] = yield spawn(function* () {
const childCTA = yield exec(run, { cwd: recipeFolder })
const cta = yield captureLogs(childCTA)
const childTauriBuild = yield exec(manager, {
arguments: opts,
cwd: appFolder
})
const tauriBuild = yield captureLogs(childTauriBuild)
return { cta, tauriBuild }
})
}
console.log(`------------------ ${recipe} complete -------------------`)
console.log('::endgroup::')
// sometimes it takes a moment to flush all of the logs
// to the console, let things catch up here
yield sleep(1000)
}
if (parallelize) {
for (let i = 0; i < recipes.length; i++) {
const recipe = recipes[i]
console.log(`::group::recipe ${recipe} logs`)
console.log(
`------------------ ${recipe} output start -------------------`
)
const out = yield output[recipe]
console.log(out.cta)
console.log(out.tauriBuild)
console.log(
`------------------ ${recipe} output end -------------------`
)
console.log('::endgroup::')
}
}
} catch (e) {
console.error(e)
throw Error(e)
} finally {
console.log('\nstopping process...')
// wait a tick for file locks to be release
yield sleep(5000)
yield fs.rm(tauriTemp, { recursive: true, force: true })
console.log(`${tauriTemp} deleted.`)
}
})
function* streamLogs(child) {
yield child.stdout.forEach((data) => {
process.stdout.write(data)
})
yield child.stderr.forEach((data) => {
process.stderr.write(data)
})
}
function* captureLogs(child) {
let log = ''
yield child.stdout.forEach((data) => {
log += data
})
yield child.stderr.forEach((data) => {
log += data
})
return log
}
function* assertCTAState({ appFolder, appName }) {
const packageFileInitial = JSON.parse(
yield fs.readFile(path.join(appFolder, 'package.json'), 'utf-8')
)
assert.strictEqual(
packageFileInitial.name,
appName,
`The package.json did not have the name "${appName}".`
)
assert.strictEqual(
packageFileInitial.scripts.tauri,
'tauri',
`The package.json did not have the tauri script.`
)
}
function* assertTauriBuildState({ appFolder, appName }) {
const packageFileOutput = JSON.parse(
yield fs.readFile(path.join(appFolder, 'package.json'), 'utf-8')
)
assert.strictEqual(
packageFileOutput.name,
appName,
`The package.json did not have the name "${appName}".`
)
assert.strictEqual(
packageFileOutput.scripts.tauri,
'tauri',
`The package.json did not have the tauri script.`
)
const cargoFileOutput = yield fs.readFile(
path.join(appFolder, 'src-tauri', 'Cargo.toml'),
'utf-8'
)
assert.strictEqual(
cargoFileOutput.startsWith(`[package]\nname = "app"`),
true,
`The Cargo.toml did not have the name "app".`
)
const tauriTarget = yield fs.readdir(
path.join(appFolder, 'src-tauri', 'target')
)
assert.strictEqual(
tauriTarget.includes('release'),
true,
`The Tauri build does not have a target/release directory.`
)
}

File diff suppressed because it is too large Load Diff