mirror of https://github.com/tauri-apps/tauri
feat: setup testing for CTA (#1615)
* feat: setup testing for CTA * install with yarn * build before test * add yarn to npm runs for install / test start * add dev mode to link cli.js and api locally * remove fixtures * run tests serially * cli.js build-release avoids webpack error * assert on package.json contents as first check * run tauri build and split out custom asserts * add changefile * shorten workflow name * too short * exclude npm@6 on node@16 * increase timeout, tauri build takes a bit of time * only assert that the tauri script exists
This commit is contained in:
parent
ddcd9233bd
commit
af6411d5f8
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"create-tauri-app": patch
|
||||
---
|
||||
|
||||
We setup an e2e type test suite for CTA. It is mostly an internal change, but should help with stability moving forward.
|
|
@ -0,0 +1,97 @@
|
|||
# Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: test create-tauri-app
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
default: "dev"
|
||||
pull_request:
|
||||
paths:
|
||||
- "tooling/create-tauri-app/**"
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
create-recipe-with-npm:
|
||||
name: "node@${{ matrix.node }} + npm@${{ matrix.manager }}: ${{ matrix.recipe }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ["14", "16"]
|
||||
manager: ["6", "7"]
|
||||
recipe: ["vanillajs", "reactjs", "reactts", "vite", "vuecli"]
|
||||
exclude:
|
||||
- node: "16"
|
||||
manager: "6"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.head_ref || github.event.inputs.branch }}
|
||||
- name: install stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: volta-cli/action@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
npm-version: ${{ matrix.manager }}
|
||||
yarn-version: 1.22.5
|
||||
- name: install webkit2gtk (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y webkit2gtk-4.0
|
||||
- run: yarn
|
||||
working-directory: tooling/create-tauri-app
|
||||
- run: yarn build
|
||||
working-directory: tooling/create-tauri-app
|
||||
- run: yarn test
|
||||
working-directory: tooling/create-tauri-app
|
||||
env:
|
||||
TAURI_RECIPE: ${{ matrix.recipe }}
|
||||
TAURI_RUN_MANAGER: "npm"
|
||||
|
||||
create-recipe-with-yarn:
|
||||
name: "node@${{ matrix.node }} + yarn@1: ${{ matrix.recipe }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: ["14", "16"]
|
||||
recipe: ["vanillajs", "reactjs", "reactts", "vite", "vuecli"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.head_ref || github.event.inputs.branch }}
|
||||
- name: install stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- uses: volta-cli/action@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
yarn-version: 1.22.5
|
||||
- name: install webkit2gtk (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y webkit2gtk-4.0
|
||||
- run: yarn
|
||||
working-directory: tooling/create-tauri-app
|
||||
- run: yarn build
|
||||
working-directory: tooling/create-tauri-app
|
||||
- run: yarn test
|
||||
working-directory: tooling/create-tauri-app
|
||||
env:
|
||||
TAURI_RECIPE: ${{ matrix.recipe }}
|
||||
TAURI_RUN_MANAGER: "yarn"
|
|
@ -7,7 +7,7 @@ module.exports = {
|
|||
},
|
||||
|
||||
parser: '@typescript-eslint/parser',
|
||||
|
||||
ignorePatterns: ['.eslintrc.js', 'jest.config.js', 'test/**/*'],
|
||||
extends: [
|
||||
'standard-with-typescript',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
|
|
|
@ -29,6 +29,7 @@ const {
|
|||
* @property {boolean} log
|
||||
* @property {boolean} d
|
||||
* @property {boolean} directory
|
||||
* @property {boolean} dev
|
||||
* @property {string} r
|
||||
* @property {string} recipe
|
||||
*/
|
||||
|
@ -41,7 +42,7 @@ const createTauriApp = async (cliArgs) => {
|
|||
l: 'log',
|
||||
m: 'manager',
|
||||
d: 'directory',
|
||||
b: 'binary',
|
||||
dev: 'dev',
|
||||
t: 'tauri-path',
|
||||
A: 'app-name',
|
||||
W: 'window-title',
|
||||
|
@ -49,7 +50,7 @@ const createTauriApp = async (cliArgs) => {
|
|||
P: 'dev-path',
|
||||
r: 'recipe'
|
||||
},
|
||||
boolean: ['h', 'l', 'ci']
|
||||
boolean: ['h', 'l', 'ci', 'dev']
|
||||
})
|
||||
|
||||
if (argv.help) {
|
||||
|
@ -62,13 +63,9 @@ const createTauriApp = async (cliArgs) => {
|
|||
return false // do this for node consumers and tests
|
||||
}
|
||||
|
||||
if (argv.ci) {
|
||||
return runInit(argv)
|
||||
} else {
|
||||
return getOptionsInteractive(argv).then((responses) =>
|
||||
runInit(argv, responses)
|
||||
)
|
||||
}
|
||||
return getOptionsInteractive(argv, !argv.ci).then((responses) =>
|
||||
runInit(argv, responses)
|
||||
)
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
|
@ -95,8 +92,12 @@ function printUsage() {
|
|||
`)
|
||||
}
|
||||
|
||||
const getOptionsInteractive = (argv) => {
|
||||
let defaultAppName = argv.A || 'tauri-app'
|
||||
const getOptionsInteractive = (argv, ask) => {
|
||||
const defaults = {
|
||||
appName: argv.A || 'tauri-app',
|
||||
tauri: { window: { title: 'Tauri App' } },
|
||||
recipeName: argv.r || 'vanillajs'
|
||||
}
|
||||
|
||||
return inquirer
|
||||
.prompt([
|
||||
|
@ -104,29 +105,33 @@ const getOptionsInteractive = (argv) => {
|
|||
type: 'input',
|
||||
name: 'appName',
|
||||
message: 'What is your app name?',
|
||||
default: defaultAppName,
|
||||
when: !argv.A
|
||||
default: defaults.appName,
|
||||
when: ask && !argv.A
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'tauri.window.title',
|
||||
message: 'What should the window title be?',
|
||||
default: 'Tauri App',
|
||||
when: () => !argv.W
|
||||
default: defaults.tauri.window.title,
|
||||
when: ask && !argv.W
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'recipeName',
|
||||
message: 'Would you like to add a UI recipe?',
|
||||
choices: recipeDescriptiveNames,
|
||||
default: 'No recipe',
|
||||
when: () => !argv.r
|
||||
default: defaults.recipeName,
|
||||
when: ask && !argv.r
|
||||
}
|
||||
])
|
||||
.then((answers) => ({
|
||||
...defaults,
|
||||
...answers
|
||||
}))
|
||||
.catch((error) => {
|
||||
if (error.isTtyError) {
|
||||
// Prompt couldn't be rendered in the current environment
|
||||
console.log(
|
||||
console.warn(
|
||||
'It appears your terminal does not support interactive prompts. Using default values.'
|
||||
)
|
||||
runInit()
|
||||
|
@ -150,10 +155,10 @@ async function runInit(argv, config = {}) {
|
|||
|
||||
let recipe
|
||||
|
||||
if (recipeName !== undefined) {
|
||||
recipe = recipeByDescriptiveName(recipeName)
|
||||
} else if (argv.r) {
|
||||
if (argv.r) {
|
||||
recipe = recipeByShortName(argv.r)
|
||||
} else if (recipeName !== undefined) {
|
||||
recipe = recipeByDescriptiveName(recipeName)
|
||||
}
|
||||
|
||||
let buildConfig = {
|
||||
|
@ -201,10 +206,21 @@ async function runInit(argv, config = {}) {
|
|||
// Vue CLI plugin automatically runs these
|
||||
if (recipe.shortName !== 'vuecli') {
|
||||
console.log('===== installing any additional needed deps =====')
|
||||
if (argv.dev) {
|
||||
await shell('yarn', ['link', '@tauri-apps/cli'], {
|
||||
cwd: appDirectory
|
||||
})
|
||||
await shell('yarn', ['link', '@tauri-apps/api'], {
|
||||
cwd: appDirectory
|
||||
})
|
||||
}
|
||||
|
||||
await install({
|
||||
appDir: appDirectory,
|
||||
dependencies: recipe.extraNpmDependencies,
|
||||
devDependencies: ['@tauri-apps/cli', ...recipe.extraNpmDevDependencies],
|
||||
devDependencies: argv.dev
|
||||
? [...recipe.extraNpmDevDependencies]
|
||||
: ['@tauri-apps/cli'].concat(recipe.extraNpmDevDependencies),
|
||||
packageManager
|
||||
})
|
||||
|
||||
|
@ -216,7 +232,7 @@ async function runInit(argv, config = {}) {
|
|||
packageManager === 'npm' && !argv.b
|
||||
? ['run', 'tauri', '--', 'init']
|
||||
: ['tauri', 'init']
|
||||
await shell(binary, [...runTauriArgs, ...initArgs], {
|
||||
await shell(binary, [...runTauriArgs, ...initArgs, '--ci'], {
|
||||
cwd: appDirectory
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
modulePathIgnorePatterns: ['__fixtures__'],
|
||||
testMatch: ['<rootDir>/test/**/*.spec.ts'],
|
||||
moduleFileExtensions: ['ts', 'js', 'json'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: 'tsconfig.json'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,8 @@
|
|||
"lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\"",
|
||||
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
|
||||
"format": "prettier --write --end-of-line=auto \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
|
||||
"format:check": "prettier --check --end-of-line=auto \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore"
|
||||
"format:check": "prettier --check --end-of-line=auto \"./**/*.{js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
|
||||
"test": "jest --runInBand"
|
||||
},
|
||||
"dependencies": {
|
||||
"execa": "^5.0.0",
|
||||
|
@ -41,11 +42,12 @@
|
|||
"@rollup/plugin-commonjs": "18.0.0",
|
||||
"@rollup/plugin-node-resolve": "11.2.1",
|
||||
"@rollup/plugin-typescript": "8.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "4.22.0",
|
||||
"@typescript-eslint/parser": "4.22.0",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
"@types/inquirer": "7.3.1",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/semver": "7.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "4.22.0",
|
||||
"@typescript-eslint/parser": "4.22.0",
|
||||
"eslint": "7.24.0",
|
||||
"eslint-config-prettier": "8.2.0",
|
||||
"eslint-config-standard-with-typescript": "20.0.0",
|
||||
|
@ -54,8 +56,11 @@
|
|||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-promise": "5.1.0",
|
||||
"eslint-plugin-security": "1.4.0",
|
||||
"fixturez": "^1.1.0",
|
||||
"jest": "^26.6.3",
|
||||
"prettier": "2.2.1",
|
||||
"rollup": "2.45.1",
|
||||
"ts-jest": "^26.5.5",
|
||||
"tslib": "2.2.0",
|
||||
"typescript": "4.2.4"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
// 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', 'reactjs', 'reactts', 'vite', 'vuecli']
|
||||
const timeoutLong = 900000
|
||||
const timeoutLittleLonger = 930000
|
||||
const logOut = false ? 'inherit' : 'pipe'
|
||||
|
||||
beforeAll(async () => {
|
||||
const installCLI = await execa('yarn', [], {
|
||||
stdio: logOut,
|
||||
cwd: clijs,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
|
||||
const buildCLI = await execa('yarn', ['build-release'], {
|
||||
stdio: logOut,
|
||||
cwd: clijs,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
|
||||
const linkCLI = await execa('yarn', ['link'], {
|
||||
stdio: logOut,
|
||||
cwd: clijs,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
|
||||
const installAPI = await execa('yarn', [], {
|
||||
stdio: logOut,
|
||||
cwd: api,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
|
||||
const buildAPI = await execa('yarn', ['build'], {
|
||||
stdio: logOut,
|
||||
cwd: api,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
|
||||
const linkAPI = await execa('yarn', ['link'], {
|
||||
stdio: logOut,
|
||||
cwd: api,
|
||||
timeout: timeoutLong
|
||||
})
|
||||
}, timeoutLittleLonger)
|
||||
|
||||
describe('CTA', () => {
|
||||
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)
|
||||
|
||||
// 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 = ['run', 'tauri', '--', 'build']
|
||||
} else if (manager === 'yarn') {
|
||||
opts = ['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'
|
||||
})
|
||||
},
|
||||
reactjs: () => {
|
||||
expect(packageFileOutput['scripts']).toEqual(
|
||||
expect.objectContaining({
|
||||
tauri: 'tauri'
|
||||
})
|
||||
)
|
||||
},
|
||||
reactts: () => {
|
||||
expect(packageFileOutput['scripts']).toEqual(
|
||||
expect.objectContaining({
|
||||
tauri: 'tauri'
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const getCustomAsserts = assertCustom[recipe]
|
||||
if (getCustomAsserts) getCustomAsserts()
|
||||
},
|
||||
timeoutLittleLonger
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
|
@ -7,8 +7,9 @@
|
|||
"pretty": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node"
|
||||
"moduleResolution": "node",
|
||||
"typeRoots": ["./types", "node_modules/@types"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/templates"]
|
||||
"exclude": ["src/templates", "types", "test", "__fixtures__"]
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
declare module 'fixturez'
|
Loading…
Reference in New Issue