Add tauri e2e test for auth on Linux (#1040)
* WIP: Add tauri e2e test for auth on Linux Fixes #968 * WIP * WIP * Working of through /tmp file for user code sharing * rust int * User code only in /tmp, fixes * Longer timeout for github actions * Remove timeout * Fmt * Fmt, 30sec timeout * Test BUILD_RELEASE true * Revert "Test BUILD_RELEASE true" This reverts commitd3b59d4a6c
. * Disable concurrency limit for faster iterations on this PR * Add logs for responses * Test manual tauri build before e2e * WIP * Catch error on tauri::api:🐚:open * Clean up * Clean up * timeout * Force BUILD_RELEASE: true * Back to debug, longer timeout * Print if url opens ok too * Check default browser in actions * Remote shell call on linux (aka e2e for now) * Fix fmt * Move to data-testid, clean up * Add log out section * Clean up * Fix typo * Fix text detection * Test AppImage * Revert "Test AppImage" This reverts commitcf126b1aa6
. * Add comments * Change to env var * Clean up * Fmt fix * Better package json name * Add import @wdio/globals * Back to require * Update wdio, fix globals * Move to typescript * A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@ -31,7 +31,6 @@ jobs:
|
|||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn fmt-check
|
- run: yarn fmt-check
|
||||||
|
|
||||||
|
|
||||||
check-types:
|
check-types:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
@ -247,18 +246,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
||||||
|
|
||||||
- name: Install tauri-driver for e2e tests (linux only)
|
|
||||||
if: matrix.os == 'ubuntu-latest'
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: install
|
|
||||||
args: tauri-driver
|
|
||||||
|
|
||||||
- name: Run e2e tests (linux only)
|
- name: Run e2e tests (linux only)
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: xvfb-run yarn test:e2e
|
run: |
|
||||||
|
cargo install tauri-driver
|
||||||
|
xvfb-run yarn test:e2e:tauri
|
||||||
env:
|
env:
|
||||||
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
|
E2E_APPLICATION: "./src-tauri/target/${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}/kittycad-modeling"
|
||||||
|
KITTYCAD_API_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
publish-apps-release:
|
publish-apps-release:
|
||||||
|
62
e2e/tauri/specs/auth.e2e.ts
Normal file
62
e2e/tauri/specs/auth.e2e.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { browser, $, expect } from '@wdio/globals'
|
||||||
|
import fs from 'fs/promises'
|
||||||
|
|
||||||
|
describe('KCMA (Tauri, Linux)', () => {
|
||||||
|
it('opens the auth page, signs in, and signs out', async () => {
|
||||||
|
// Clean up previous tests
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
|
await fs.rm('/tmp/kittycad_user_code', { force: true })
|
||||||
|
await browser.execute('window.localStorage.clear()')
|
||||||
|
|
||||||
|
const signInButton = await $('[data-testid="sign-in-button"]')
|
||||||
|
expect(await signInButton.getText()).toEqual('Sign in')
|
||||||
|
|
||||||
|
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
||||||
|
await signInButton.waitForClickable()
|
||||||
|
await browser.execute('arguments[0].click();', signInButton)
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
|
||||||
|
// Get from main.rs
|
||||||
|
const userCode = await (
|
||||||
|
await fs.readFile('/tmp/kittycad_user_code')
|
||||||
|
).toString()
|
||||||
|
console.log(`Found user code ${userCode}`)
|
||||||
|
|
||||||
|
// Device flow: verify
|
||||||
|
const token = process.env.KITTYCAD_API_TOKEN
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
}
|
||||||
|
const verifyUrl = `https://api.kittycad.io/oauth2/device/verify?user_code=${userCode}`
|
||||||
|
console.log(`GET ${verifyUrl}`)
|
||||||
|
const vr = await fetch(verifyUrl, { headers })
|
||||||
|
console.log(vr.status)
|
||||||
|
|
||||||
|
// Device flow: confirm
|
||||||
|
const confirmUrl = 'https://api.kittycad.io/oauth2/device/confirm'
|
||||||
|
const data = JSON.stringify({ user_code: userCode })
|
||||||
|
console.log(`POST ${confirmUrl} ${data}`)
|
||||||
|
const cr = await fetch(confirmUrl, {
|
||||||
|
headers,
|
||||||
|
method: 'POST',
|
||||||
|
body: data,
|
||||||
|
})
|
||||||
|
console.log(cr.status)
|
||||||
|
|
||||||
|
// Now should be signed in
|
||||||
|
const newFileButton = await $('[data-testid="home-new-file"]')
|
||||||
|
expect(await newFileButton.getText()).toEqual('New file')
|
||||||
|
|
||||||
|
// So let's sign out!
|
||||||
|
const menuButton = await $('[data-testid="user-sidebar-toggle"]')
|
||||||
|
await menuButton.waitForClickable()
|
||||||
|
await browser.execute('arguments[0].click();', menuButton)
|
||||||
|
const signoutButton = await $('[data-testid="user-sidebar-sign-out"]')
|
||||||
|
await signoutButton.waitForClickable()
|
||||||
|
await browser.execute('arguments[0].click();', signoutButton)
|
||||||
|
const newSignInButton = await $('[data-testid="sign-in-button"]')
|
||||||
|
expect(await newSignInButton.getText()).toEqual('Sign in')
|
||||||
|
})
|
||||||
|
})
|
@ -1,11 +0,0 @@
|
|||||||
describe('Modeling App', () => {
|
|
||||||
it('open the sign in page', async () => {
|
|
||||||
const button = await $('#signin')
|
|
||||||
expect(button).toHaveText('Sign in')
|
|
||||||
|
|
||||||
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
|
||||||
await button.waitForClickable()
|
|
||||||
await browser.execute('arguments[0].click();', button)
|
|
||||||
// TODO: handle auth
|
|
||||||
})
|
|
||||||
})
|
|
11
package.json
11
package.json
@ -70,7 +70,7 @@
|
|||||||
"test:nowatch": "vitest run --mode development",
|
"test:nowatch": "vitest run --mode development",
|
||||||
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
||||||
"test:cov": "vitest run --coverage --mode development",
|
"test:cov": "vitest run --coverage --mode development",
|
||||||
"test:e2e": "wdio run wdio.conf.js",
|
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||||
"fmt": "prettier --write ./src && prettier --write ./e2e",
|
"fmt": "prettier --write ./src && prettier --write ./e2e",
|
||||||
@ -116,10 +116,11 @@
|
|||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-react": "^4.1.1",
|
"@vitejs/plugin-react": "^4.1.1",
|
||||||
"@vitest/coverage-istanbul": "^0.34.6",
|
"@vitest/coverage-istanbul": "^0.34.6",
|
||||||
"@wdio/cli": "^7.7.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
"@wdio/local-runner": "^7.7.3",
|
"@wdio/globals": "^8.24.3",
|
||||||
"@wdio/mocha-framework": "^7.7.3",
|
"@wdio/local-runner": "^8.24.3",
|
||||||
"@wdio/spec-reporter": "^7.7.3",
|
"@wdio/mocha-framework": "^8.24.3",
|
||||||
|
"@wdio/spec-reporter": "^8.24.2",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.53.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@ -70,8 +72,24 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
|
|||||||
// Open the system browser with the auth_uri.
|
// Open the system browser with the auth_uri.
|
||||||
// We do this in the browser and not a separate window because we want 1password and
|
// We do this in the browser and not a separate window because we want 1password and
|
||||||
// other crap to work well.
|
// other crap to work well.
|
||||||
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
|
// TODO: find a better way to share this value with tauri e2e tests
|
||||||
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
// Here we're using an env var to enable the /tmp file (windows not supported for now)
|
||||||
|
// and bypass the shell::open call as it fails on GitHub Actions.
|
||||||
|
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
|
||||||
|
if (e2e_tauri_enabled) {
|
||||||
|
println!(
|
||||||
|
"E2E_TAURI_ENABLED is set, won't open {} externally",
|
||||||
|
auth_uri.secret()
|
||||||
|
);
|
||||||
|
fs::write(
|
||||||
|
"/tmp/kittycad_user_code",
|
||||||
|
details.user_code().secret().to_string(),
|
||||||
|
)
|
||||||
|
.expect("Unable to write /tmp/kittycad_user_code file");
|
||||||
|
} else {
|
||||||
|
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
|
||||||
|
.map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for the user to login.
|
// Wait for the user to login.
|
||||||
let token = auth_client
|
let token = auth_client
|
||||||
|
@ -165,6 +165,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
|||||||
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
|
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
|
||||||
}}
|
}}
|
||||||
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60"
|
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60"
|
||||||
|
data-testid="user-sidebar-sign-out"
|
||||||
>
|
>
|
||||||
Sign out
|
Sign out
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
@ -255,6 +255,7 @@ const Home = () => {
|
|||||||
Element="button"
|
Element="button"
|
||||||
onClick={() => send('Create project')}
|
onClick={() => send('Create project')}
|
||||||
icon={{ icon: faPlus }}
|
icon={{ icon: faPlus }}
|
||||||
|
data-testid="home-new-file"
|
||||||
>
|
>
|
||||||
New file
|
New file
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
@ -67,7 +67,7 @@ const SignIn = () => {
|
|||||||
onClick={signInTauri}
|
onClick={signInTauri}
|
||||||
icon={{ icon: faSignInAlt }}
|
icon={{ icon: faSignInAlt }}
|
||||||
className="w-fit mt-4"
|
className="w-fit mt-4"
|
||||||
id="signin"
|
data-testid="sign-in-button"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"/*": ["src/*"]
|
"/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"types": ["vite/client", "@types/wicg-file-system-access"],
|
"types": ["vite/client", "@types/wicg-file-system-access", "node", "@wdio/globals/types"],
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"lib": [
|
"lib": [
|
||||||
"dom",
|
"dom",
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
const os = require('os')
|
import os from 'os'
|
||||||
const path = require('path')
|
import path from 'path'
|
||||||
const { spawn } = require('child_process')
|
import { spawn, ChildProcess } from 'child_process'
|
||||||
|
|
||||||
|
let tauriDriver: ChildProcess
|
||||||
|
|
||||||
|
|
||||||
// keep track of the `tauri-driver` child process
|
|
||||||
let tauriDriver
|
|
||||||
|
|
||||||
const mode = process.env.MODE
|
|
||||||
const application =
|
const application =
|
||||||
process.env.E2E_APPLICATION || `./src-tauri/target/${mode}/kittycad-modeling`
|
process.env.E2E_APPLICATION || `./src-tauri/target/release/kittycad-modeling`
|
||||||
|
|
||||||
exports.config = {
|
export const config = {
|
||||||
port: 4444,
|
port: 4444,
|
||||||
specs: ['./e2e/tauri/specs/**/*.js'],
|
specs: ['./e2e/tauri/specs/**/*.ts'],
|
||||||
maxInstances: 1,
|
maxInstances: 1,
|
||||||
capabilities: [
|
capabilities: [
|
||||||
{
|
{
|
Reference in New Issue
Block a user