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 commit d3b59d4a6c.

* 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 commit cf126b1aa6.

* 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:
Pierre Jacquier
2023-11-29 05:15:04 -05:00
committed by GitHub
parent 58d1303468
commit cef29013b8
11 changed files with 1498 additions and 1221 deletions

View File

@ -31,7 +31,6 @@ jobs:
- run: yarn install
- run: yarn fmt-check
check-types:
runs-on: ubuntu-latest
@ -247,18 +246,14 @@ jobs:
with:
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)
if: matrix.os == 'ubuntu-latest'
run: xvfb-run yarn test:e2e
run: |
cargo install tauri-driver
xvfb-run yarn test:e2e:tauri
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:

View 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')
})
})

View File

@ -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
})
})

View File

@ -70,7 +70,7 @@
"test:nowatch": "vitest run --mode development",
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
"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": "yarn pretest && http-server ./public --cors -p 3000",
"fmt": "prettier --write ./src && prettier --write ./e2e",
@ -116,10 +116,11 @@
"@types/ws": "^8.5.5",
"@vitejs/plugin-react": "^4.1.1",
"@vitest/coverage-istanbul": "^0.34.6",
"@wdio/cli": "^7.7.3",
"@wdio/local-runner": "^7.7.3",
"@wdio/mocha-framework": "^7.7.3",
"@wdio/spec-reporter": "^7.7.3",
"@wdio/cli": "^8.24.3",
"@wdio/globals": "^8.24.3",
"@wdio/local-runner": "^8.24.3",
"@wdio/mocha-framework": "^8.24.3",
"@wdio/spec-reporter": "^8.24.2",
"autoprefixer": "^10.4.13",
"eslint": "^8.53.0",
"eslint-config-react-app": "^7.0.1",

View File

@ -1,6 +1,8 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::env;
use std::fs;
use std::io::Read;
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.
// We do this in the browser and not a separate window because we want 1password and
// other crap to work well.
// TODO: find a better way to share this value with tauri e2e tests
// 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.
let token = auth_client

View File

@ -165,6 +165,7 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
'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"
data-testid="user-sidebar-sign-out"
>
Sign out
</ActionButton>

View File

@ -255,6 +255,7 @@ const Home = () => {
Element="button"
onClick={() => send('Create project')}
icon={{ icon: faPlus }}
data-testid="home-new-file"
>
New file
</ActionButton>

View File

@ -67,7 +67,7 @@ const SignIn = () => {
onClick={signInTauri}
icon={{ icon: faSignInAlt }}
className="w-fit mt-4"
id="signin"
data-testid="sign-in-button"
>
Sign in
</ActionButton>

View File

@ -4,7 +4,7 @@
"paths": {
"/*": ["src/*"]
},
"types": ["vite/client", "@types/wicg-file-system-access"],
"types": ["vite/client", "@types/wicg-file-system-access", "node", "@wdio/globals/types"],
"target": "esnext",
"lib": [
"dom",

View File

@ -1,19 +1,15 @@
const os = require('os')
const path = require('path')
const { spawn } = require('child_process')
import os from 'os'
import path from 'path'
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 =
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,
specs: ['./e2e/tauri/specs/**/*.js'],
specs: ['./e2e/tauri/specs/**/*.ts'],
maxInstances: 1,
capabilities: [
{

2574
yarn.lock

File diff suppressed because it is too large Load Diff