import { browser, $, expect } from '@wdio/globals' import fs from 'fs/promises' import path from 'path' import os from 'os' const isWin32 = os.platform() === 'win32' const documentsDir = path.join(os.homedir(), 'Documents') const userSettingsDir = path.join( os.homedir(), '.config', 'dev.zoo.modeling-app' ) const defaultProjectDir = path.join(documentsDir, 'zoo-modeling-app-projects') const newProjectDir = path.join(documentsDir, 'a-different-directory') const tmp = process.env.TEMP || '/tmp' const userCodeDir = path.join(tmp, 'kittycad_user_code') async function click(element: WebdriverIO.Element): Promise { // Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541 await element.waitForClickable() await browser.execute('arguments[0].click();', element) } /* Shoutout to @Sheap on Github for a great workaround utility: * https://github.com/tauri-apps/tauri/issues/6541#issue-1638944060 */ async function setDatasetValue( field: WebdriverIO.Element, property: string, value: string ) { await browser.execute(`arguments[0].dataset.${property} = "${value}"`, field) } describe('ZMA (Tauri)', () => { it('opens the auth page and signs in', async () => { // Clean up filesystem from previous tests await new Promise((resolve) => setTimeout(resolve, 100)) await fs.rm(defaultProjectDir, { force: true, recursive: true }) await fs.rm(newProjectDir, { force: true, recursive: true }) await fs.rm(userCodeDir, { force: true }) await fs.rm(userSettingsDir, { force: true, recursive: true }) await fs.mkdir(defaultProjectDir, { recursive: true }) await fs.mkdir(newProjectDir, { recursive: true }) const signInButton = await $('[data-testid="sign-in-button"]') expect(await signInButton.getText()).toEqual('Sign in') await click(signInButton) await new Promise((resolve) => setTimeout(resolve, 2000)) // Get from main.rs const userCode = await (await fs.readFile(userCodeDir)).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 apiBaseUrl = process.env.VITE_KC_API_BASE_URL const verifyUrl = `${apiBaseUrl}/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 = `${apiBaseUrl}/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 await new Promise((resolve) => setTimeout(resolve, 10000)) const newFileButton = await $('[data-testid="home-new-file"]') expect(await newFileButton.getText()).toEqual('New project') }) it('opens the settings page, checks filesystem settings, and closes the settings page', async () => { const menuButton = await $('[data-testid="user-sidebar-toggle"]') await click(menuButton) const settingsButton = await $('[data-testid="settings-button"]') await click(settingsButton) const projectDirInput = await $('[data-testid="project-directory-input"]') expect(await projectDirInput.getValue()).toEqual(defaultProjectDir) /* * We've set up the project directory input (in initialSettings.tsx) * to be able to skip the folder selection dialog if data-testValue * has a value, allowing us to test the input otherwise works. */ // TODO: understand why we need to force double \ on Windows await setDatasetValue( projectDirInput, 'testValue', isWin32 ? newProjectDir.replaceAll('\\', '\\\\') : newProjectDir ) const projectDirButton = await $('[data-testid="project-directory-button"]') await click(projectDirButton) await new Promise((resolve) => setTimeout(resolve, 500)) // This line is broken. I need a different way to grab the toast await expect(await $('div*=Set project directory to')).toBeDisplayed() const nameInput = await $('[data-testid="projects-defaultProjectName"]') expect(await nameInput.getValue()).toEqual('project-$nnn') // Setting it back (for back to back local tests) await new Promise((resolve) => setTimeout(resolve, 5000)) await setDatasetValue( projectDirInput, 'testValue', isWin32 ? defaultProjectDir.replaceAll('\\', '\\\\') : newProjectDir ) await click(projectDirButton) const closeButton = await $('[data-testid="settings-close-button"]') await click(closeButton) }) it('checks that no file exists, creates a new file', async () => { const homeSection = await $('[data-testid="home-section"]') expect(await homeSection.getText()).toContain('No Projects found') const newFileButton = await $('[data-testid="home-new-file"]') await click(newFileButton) await new Promise((resolve) => setTimeout(resolve, 1000)) expect(await homeSection.getText()).toContain('project-000') }) it('opens the new file and expects a loading stream', async () => { const projectLink = await $('[data-testid="project-link"]') await click(projectLink) if (isWin32) { // TODO: actually do something to check that the stream is up await new Promise((resolve) => setTimeout(resolve, 5000)) } else { const errorText = await $('[data-testid="unexpected-error"]') expect(await errorText.getText()).toContain('unexpected error') } const base = isWin32 ? 'http://tauri.localhost' : 'tauri://localhost' await browser.execute(`window.location.href = "${base}/home"`) }) it('signs out', async () => { await new Promise((resolve) => setTimeout(resolve, 1000)) const menuButton = await $('[data-testid="user-sidebar-toggle"]') await click(menuButton) const signoutButton = await $('[data-testid="user-sidebar-sign-out"]') await click(signoutButton) const newSignInButton = await $('[data-testid="sign-in-button"]') expect(await newSignInButton.getText()).toEqual('Sign in') }) })