Unify Frontend App (#11287)

Fixes #10668
Fixes #8484

Summary of changes:
* `gui2` and `dashboard` are merged to `gui` directory. Various configs were merged (package.json, playwrigth, TS...). The src and e2e directories are split to `dashboard` and `project-view` for now.
* E2E tests run two servers on different ports. The tests are organized in projects. This is also to be changed soon, as we plan to [use better mocking in GUI/ProjectView](#9726)
* ESlint configs were merged to central `eslint.config.mjs`, and that file was moved to repository root. We kept the dashboard lints, but they can be relaxed. The dashboard code was changed to meet GUI lints.
* Also, the versions of linter plugins were bumped, and code fixed.
* The ide-desktop/client no longer has `dashboard` dependency - the only type used there was moved to common package.
* `common` package moved to `app`.
This commit is contained in:
Adam Obuchowicz 2024-10-11 20:23:02 +02:00 committed by GitHub
parent 204b37c6c3
commit 4a249688e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1130 changed files with 2794 additions and 4492 deletions

31
.github/CODEOWNERS vendored
View File

@ -19,14 +19,30 @@ Cargo.toml
/lib/rust/parser/ @farmaazon @kazcw @vitvakatu @Frizi @jaroslavtulach @AdRiley /lib/rust/parser/ @farmaazon @kazcw @vitvakatu @Frizi @jaroslavtulach @AdRiley
/tools/build-performance/ @kazcw @Akirathan /tools/build-performance/ @kazcw @Akirathan
# Global JS configuration
esling.config.mjs
tsconfig.json
# Scala Libraries # Scala Libraries
/lib/scala/ @4e6 @jaroslavtulach @hubertp @Akirathan /lib/scala/ @4e6 @jaroslavtulach @hubertp @Akirathan
# Java libraries # Java libraries
/lib/java/ @4e6 @jaroslavtulach @hubertp @Akirathan /lib/java/ @4e6 @jaroslavtulach @hubertp @Akirathan
# GUI # GUI/Dashboard
/app/gui2/ @Frizi @farmaazon @vitvakatu @kazcw @AdRiley /app @Frizi @farmaazon @vitvakatu @kazcw @AdRiley @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/gui/e2e/dashboard @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/gui/e2e/project-view @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
/app/gui/src/dashboard @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/gui/src/project-view @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
/app/ide-desktop/ @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/ydoc-server/ @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
/app/ydoc-server-nodejs/ @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
/app/ydoc-server-polyglot/ @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
/app/ydoc-shared/ @Frizi @farmaazon @vitvakatu @kazcw @AdRiley
# The data-link schema is owned by the libraries team
/app/gui/src/dashboard/data/datalinkSchema.json @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey
/app/gui/src/dashboard/data/__tests__ @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
# Engine (old) # Engine (old)
# This section should be removed once the engine moves to /app/engine # This section should be removed once the engine moves to /app/engine
@ -45,14 +61,3 @@ Cargo.toml
# The default project template is owned by the libraries team # The default project template is owned by the libraries team
/lib/scala/pkg/src/main/resources/default/src/ @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey /lib/scala/pkg/src/main/resources/default/src/ @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey
# Dashboard, Cloud, Authentication & Electron
/app/ide-desktop/ @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/dashboard/ @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
# The data-link schema is owned by the libraries team
/app/dashboard/src/data/datalinkSchema.json @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey
/app/dashboard/src/data/__tests__ @radeusgd @jdunkerley @GregoryTravis @AdRiley @marthasharkey @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
# GUI / Dashboard shared
/app/*.* @Frizi @farmaazon @vitvakatu @kazcw @AdRiley @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount
/app/ide-desktop/common @PabloBuchu @indiv0 @somebody1234 @MrFlashAccount @Frizi @farmaazon @vitvakatu @kazcw @AdRiley

View File

@ -39,10 +39,10 @@ app/ide-desktop/lib/dashboard/playwright/.cache/
app/ide-desktop/lib/dashboard/dist/ app/ide-desktop/lib/dashboard/dist/
app/gui/view/documentation/assets/stylesheet.css app/gui/view/documentation/assets/stylesheet.css
app/rust-ffi/pkg app/rust-ffi/pkg
app/gui2/src/assets/font-*.css app/gui/src/project-view/assets/font-*.css
Cargo.lock Cargo.lock
build.json build.json
app/gui2/playwright-report/ app/gui/playwright-report/
# Engine Builds can leave these nested working copies. # Engine Builds can leave these nested working copies.
# TODO [mwu]: Adjust Engine build to not leave them. # TODO [mwu]: Adjust Engine build to not leave them.

View File

@ -12,7 +12,7 @@
} }
], ],
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"eslint.experimental.useFlatConfig": true, "eslint.useFlatConfig": true,
"eslint.useESLintClass": true, "eslint.useESLintClass": true,
"[javascript][typescript][typescriptreact][vue]": { "[javascript][typescript][typescriptreact][vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",

View File

@ -21,7 +21,7 @@
<img src="https://img.shields.io/static/v1?label=Compiler%20License&message=Apache%20v2&color=2ec352&labelColor=2c3239" <img src="https://img.shields.io/static/v1?label=Compiler%20License&message=Apache%20v2&color=2ec352&labelColor=2c3239"
alt="License"> alt="License">
</a> </a>
<a href="https://github.com/enso-org/enso/blob/develop/app/gui2/LICENSE"> <a href="https://github.com/enso-org/enso/blob/develop/app/gui/LICENSE">
<img src="https://img.shields.io/static/v1?label=GUI%20License&message=AGPL%20v3&color=2ec352&labelColor=2c3239" <img src="https://img.shields.io/static/v1?label=GUI%20License&message=AGPL%20v3&color=2ec352&labelColor=2c3239"
alt="License"> alt="License">
</a> </a>
@ -207,11 +207,11 @@ Enso consists of several sub projects:
command line tools. command line tools.
- **Enso IDE:** The - **Enso IDE:** The
[Enso IDE](https://github.com/enso-org/enso/tree/develop/app/gui2) is a [Enso IDE](https://github.com/enso-org/enso/tree/develop/app/gui) is a desktop
desktop application that allows working with the visual form of Enso. It application that allows working with the visual form of Enso. It consists of
consists of an Electron application, a high performance WebGL UI framework, an Electron application, a high performance WebGL UI framework, and the
and the searcher which provides contextual search, hints, and documentation searcher which provides contextual search, hints, and documentation for all of
for all of Enso's functionality. Enso's functionality.
<br/> <br/>
@ -222,7 +222,7 @@ The Enso Engine is licensed under the
[LICENSE](https://github.com/enso-org/enso/blob/develop/LICENSE) file. The Enso [LICENSE](https://github.com/enso-org/enso/blob/develop/LICENSE) file. The Enso
IDE is licensed under the [AGPL 3.0](https://opensource.org/licenses/AGPL-3.0), IDE is licensed under the [AGPL 3.0](https://opensource.org/licenses/AGPL-3.0),
as specified in the as specified in the
[LICENSE](https://github.com/enso-org/enso/blob/develop/app/gui2/LICENSE) file. [LICENSE](https://github.com/enso-org/enso/blob/develop/app/gui/LICENSE) file.
This license set was chosen to provide you with complete freedom to use Enso, This license set was chosen to provide you with complete freedom to use Enso,
create libraries, and release them under any license of your choice, while also create libraries, and release them under any license of your choice, while also

View File

@ -46,7 +46,7 @@
"request": "launch", "request": "launch",
"name": "GUI (Storybook)", "name": "GUI (Storybook)",
"runtimeExecutable": "pnpm", "runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "--filter", "enso-gui2", "story:dev"], "runtimeArgs": ["run", "--filter", "enso-gui", "story:dev"],
"outputCapture": "std" "outputCapture": "std"
}, },
{ {
@ -70,7 +70,7 @@
"request": "launch", "request": "launch",
"name": "GUI (E2E UI)", "name": "GUI (E2E UI)",
"runtimeExecutable": "pnpm", "runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "--filter", "enso-gui2", "test:e2e", "--", "--ui"], "runtimeArgs": ["run", "--filter", "enso-gui", "test:e2e", "--", "--ui"],
"outputCapture": "std" "outputCapture": "std"
}, },
{ {
@ -102,14 +102,14 @@
"request": "launch", "request": "launch",
"name": "GUI (All tests)", "name": "GUI (All tests)",
"runtimeExecutable": "pnpm", "runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "--filter", "enso-gui2", "test"] "runtimeArgs": ["run", "--filter", "enso-gui", "test"]
}, },
{ {
"type": "node", "type": "node",
"request": "launch", "request": "launch",
"name": "GUI (E2E tests)", "name": "GUI (E2E tests)",
"runtimeExecutable": "pnpm", "runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "--filter", "enso-gui2", "test:e2e"], "runtimeArgs": ["run", "--filter", "enso-gui", "test:e2e"],
"outputCapture": "std" "outputCapture": "std"
}, },
{ {
@ -117,7 +117,7 @@
"request": "launch", "request": "launch",
"name": "GUI (Unit tests)", "name": "GUI (Unit tests)",
"runtimeExecutable": "pnpm", "runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "--filter", "enso-gui2", "test:unit", "--", "run"], "runtimeArgs": ["run", "--filter", "enso-gui", "test:unit", "--", "run"],
"outputCapture": "std" "outputCapture": "std"
} }
] ]

View File

@ -6,6 +6,7 @@
"exports": { "exports": {
".": "./src/index.js", ".": "./src/index.js",
"./src/config.json": "./src/config.json", "./src/config.json": "./src/config.json",
"./src/accessToken": "./src/accessToken.ts",
"./src/appConfig": "./src/appConfig.js", "./src/appConfig": "./src/appConfig.js",
"./src/buildUtils": "./src/buildUtils.js", "./src/buildUtils": "./src/buildUtils.js",
"./src/detect": "./src/detect.ts", "./src/detect": "./src/detect.ts",

View File

@ -445,8 +445,7 @@ export interface CheckoutSessionStatus {
/** Status of the payment for the checkout session. */ /** Status of the payment for the checkout session. */
readonly paymentStatus: string readonly paymentStatus: string
/** Status of the checkout session. */ /** Status of the checkout session. */
// eslint-disable-next-line @typescript-eslint/ban-types readonly status: 'active' | 'trialing' | (string & NonNullable<unknown>)
readonly status: 'active' | 'trialing' | (string & {})
} }
/** Resource usage of a VM. */ /** Resource usage of a VM. */

View File

@ -1,4 +0,0 @@
playwright-report/
playwright/.cache/
test-results/
dist/

View File

@ -1,44 +0,0 @@
<!--
FIXME [NP]: https://github.com/enso-org/cloud-v2/issues/345
This file is used by both the `content` and `dashboard` packages. The `dashboard` package uses it
via a symlink. This is temporary, while the `content` and `dashboard` have separate entrypoints
for cloud and desktop. Once they are merged, the symlink must be removed.
-->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<!-- FIXME https://github.com/validator/validator/issues/917 -->
<!-- FIXME Security Vulnerabilities: https://github.com/enso-org/ide/issues/226 -->
<!-- NOTE `frame-src` section of `http-equiv` required only for authorization -->
<meta
http-equiv="Content-Security-Policy"
content="
default-src 'self';
frame-src 'self' data: https://accounts.google.com https://enso-org.firebaseapp.com;
script-src 'self' 'unsafe-eval' data: https://*;
style-src 'self' 'unsafe-inline' data: https://*;
connect-src 'self' data: ws://localhost:* ws://127.0.0.1:* http://localhost:* https://* wss://*;
worker-src 'self' blob:;
img-src 'self' blob: data: https://*;
font-src 'self' data: https://*"
/>
<meta
name="viewport"
content="
width=device-width,
initial-scale = 1.0,
maximum-scale = 1.0,
user-scalable = no"
/>
<title>Enso</title>
<script type="module" src="./src/entrypoint.ts" defer></script>
</head>
<body>
<div id="enso-dashboard" class="enso-dashboard"></div>
<div id="enso-chat" class="enso-chat"></div>
<div id="enso-portal-root" class="enso-portal-root"></div>
<noscript> This page requires JavaScript to run. Please enable it in your browser. </noscript>
</body>
</html>

View File

@ -1,76 +0,0 @@
# Dashboard
The dashboard is the entrypoint into the application. It includes project
management, project sharing, and user accounts and authentication.
## Further documentation
Further documentation is provided in the `docs/` folder:
- [Browser-specific behavior](./docs/browser_specific_behavior.md) details
behavior that is inconsistent between browsers and needs to be worked around.
## Folder structure
- `mock/`: Overrides for specific files in `src/` when running Playwright tests.
- `e2e/`: Contains end-to-end tests.
- `**/__tests__/`: Contains all unit tests. Unit tests MUST be in a `__tests__/`
subfolder, not beside (and not inside) the module they are testing.
- `src/`: The dashboard application.
- `index.html`: The sole HTML file used by this SPA. It imports the TS entry
point.
- `authentication/src/`: The main body of the app.
- `index.tsx`: The TS entry point.
- `providers/`: Contains React `Context`s used by the main app.
- `components/`: Contains the root component for the app.
- `dashboard/`: The main body of the app. Directly in the folder, there are
some utility modules that do not belong elsewhere.
- `components/`: Contains all components used by the main app.
- `events/`: Custom discriminated unions used to communicate messages
between unrelated components.
- `authentication/`: The authentication flow. This includes login,
registration, and changing passwords.
- `components/`: Contains all components used by the authentication flow.
- `providers/`: Contains React `Context`s required for authentication, and
used by the main app.
- `index.html`: The entrypoint, in the format required by Vite.
- `404.html`: A copy of the entrypoint. This is served on unknown routes by
certain static hosting providers.
- `esbuild-config.ts`: Configuration for ESBuild based on the environment
variables. This is a dependency of `esbuild-config.ts` in sibling modules.
## Cloud environment variables
These are environment variables related to the cloud backend. If these variables
are not set, the build will still work, however access to the cloud backend will
be disabled.
Note that `ENSO_CLOUD_ENVIRONMENT` may be set to instead load the files from a
`.env` file. If `ENSO_CLOUD_ENVIRONMENT` is not set, or it is `production` or
`''`, then variables are attempted to be read from `.env`. If it is set to any
other value (say, `foo`), then it is loaded from `.foo.env`.
(While the convention in the Node.js ecosystem is to name the variants like
`.env.foo`, `.foo.env` has been chosen here because `.env` should be more like
a file extension. Visual Studio Code also understands `.foo.env` but not
`.env.foo`.)
- `ENSO_CLOUD_REDIRECT`: The domain (or `localhost:8080`) where the login link
should redirect. Should include neither a path, nor a trailing slash.
- `ENSO_CLOUD_ENVIRONMENT`: The name of backend environment matching the
provided configuration keys. For most builds this should be `production`,
meaning that requests go to the production cloud backend.
- `ENSO_CLOUD_API_URL`: The root path for all API endpoints. Should not include
a trailing slash.
- `ENSO_CLOUD_SENTRY_DSN`: The Sentry Data Source Name (DSN) for this
environment. This should normally be the same for all environments.
- `ENSO_CLOUD_STRIPE_KEY`: Stripe's publishable client-side key.
- `ENSO_CLOUD_CHAT_URL`: The URL for the WebSocket server serving as the chat
backend.
- `ENSO_CLOUD_COGNITO_USER_POOL_ID`: The ID of the Cognito user pool.
- `ENSO_CLOUD_COGNITO_USER_POOL_WEB_CLIENT_ID`: The client-side key of the
Cognito user pool.
- `ENSO_CLOUD_COGNITO_DOMAIN`: The domain which all Cognito requests should go
to.
- `ENSO_CLOUD_COGNITO_REGION`: The AWS region for which Cognito is configured.
Should match the region of the domain in `ENSO_CLOUD_COGNITO_DOMAIN`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,105 +0,0 @@
{
"name": "enso-dashboard",
"version": "0.1.0",
"type": "module",
"main": "./src/index.tsx",
"private": true,
"imports": {
"#/*": "./src/*"
},
"exports": {
".": "./src/index.tsx",
"./tailwind.config": "./tailwind.config.js",
"./src/platform": "./src/platform.ts",
"./src/tailwind.css": "./src/tailwind.css"
},
"scripts": {
"compile": "tsc",
"typecheck": "tsc",
"build": "vite build",
"lint": "eslint .",
"dev": "vite",
"dev:e2e": "vite -c vite.test.config.ts",
"dev:e2e:ci": "vite -c vite.test.config.ts build && vite preview --port 8080 --strictPort",
"test": "corepack pnpm run /^^^^test:.*/",
"test:unit": "vitest run",
"test-dev:unit": "vitest",
"test:e2e": "cross-env NODE_ENV=production playwright test",
"test-dev:e2e": "cross-env NODE_ENV=production playwright test --ui"
},
"dependencies": {
"@aws-amplify/auth": "5.6.5",
"@aws-amplify/core": "5.8.5",
"@hookform/resolvers": "^3.4.0",
"@internationalized/date": "^3.5.5",
"@monaco-editor/react": "4.6.0",
"@react-aria/interactions": "^3.22.3",
"@sentry/react": "^7.74.0",
"@stripe/react-stripe-js": "^2.7.1",
"@stripe/stripe-js": "^3.5.0",
"@tanstack/react-query": "5.55.0",
"@tanstack/vue-query": ">= 5.54.0 < 5.56.0",
"ajv": "^8.12.0",
"amazon-cognito-identity-js": "6.3.6",
"clsx": "^2.1.1",
"enso-common": "workspace:*",
"framer-motion": "11.3.0",
"input-otp": "1.2.4",
"is-network-error": "^1.0.1",
"monaco-editor": "0.48.0",
"qrcode.react": "3.1.0",
"react": "^18.3.1",
"react-aria": "^3.34.3",
"react-aria-components": "^1.3.3",
"react-dom": "^18.3.1",
"react-error-boundary": "4.0.13",
"react-hook-form": "^7.51.4",
"react-router": "^6.23.1",
"react-router-dom": "^6.23.1",
"react-stately": "^3.32.2",
"react-toastify": "^9.1.3",
"tailwind-merge": "^2.3.0",
"tailwind-variants": "0.2.1",
"tiny-invariant": "^1.3.3",
"ts-results": "^3.3.0",
"validator": "^13.12.0",
"zod": "^3.23.8",
"zustand": "^4.5.4"
},
"devDependencies": {
"@fast-check/vitest": "^0.0.8",
"@modyfi/vite-plugin-yaml": "^1.0.4",
"@playwright/test": "^1.40.0",
"@react-types/shared": "^3.22.1",
"@tanstack/react-query-devtools": "5.45.1",
"@types/eslint__js": "^8.42.3",
"@types/node": "^20.11.21",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/validator": "^13.11.7",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@vitejs/plugin-react": "^4.2.1",
"chalk": "^5.3.0",
"cross-env": "^7.0.3",
"enso-chat": "git://github.com/enso-org/enso-bot",
"eslint": "^8.49.0",
"eslint-plugin-react": "^7.32.1",
"fast-check": "^3.15.0",
"playwright": "^1.38.0",
"postcss": "^8.4.29",
"prettier-plugin-organize-imports": "^4.0.0",
"prettier-plugin-tailwindcss": "^0.5.11",
"react-toastify": "^9.1.3",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "1.0.7",
"tailwindcss-react-aria-components": "^1.1.1",
"typescript": "^5.5.3",
"vite": "^5.3.5",
"vitest": "^1.3.1"
},
"overrides": {
"@aws-amplify/auth": "../_IGNORED_",
"react-native-url-polyfill": "../_IGNORED_"
}
}

View File

@ -1,64 +0,0 @@
/** @file Playwright browser testing configuration. */
/** Note that running Playwright in CI poses a number of issues:
* - `backdrop-filter: blur` is disabled, due to issues with Chromium's `--disable-gpu` flag
* (see below).
* - System validation dialogs are not reliable between computers, as they may have different
* default fonts. */
import * as test from '@playwright/test'
import * as appConfig from 'enso-common/src/appConfig'
appConfig.loadTestEnvironmentVariables()
/* eslint-disable @typescript-eslint/no-magic-numbers, @typescript-eslint/strict-boolean-expressions */
const DEBUG = process.env.PWDEBUG === '1'
const TIMEOUT_MS = DEBUG ? 100_000_000 : 30_000
export default test.defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: true,
workers: process.env.PROD ? 8 : 1,
repeatEach: process.env.CI ? 3 : 1,
expect: {
toHaveScreenshot: { threshold: 0 },
timeout: TIMEOUT_MS,
},
timeout: TIMEOUT_MS,
reporter: 'html',
use: {
baseURL: 'http://localhost:8080',
trace: 'retain-on-failure',
launchOptions: {
ignoreDefaultArgs: ['--headless'],
args: [
...(DEBUG ?
[]
: [
// Much closer to headful Chromium than classic headless.
'--headless=new',
]),
// Required for `backdrop-filter: blur` to work.
'--use-angle=swiftshader',
// FIXME: `--disable-gpu` disables `backdrop-filter: blur`, which is not handled by
// the software (CPU) compositor. This SHOULD be fixed eventually, but this flag
// MUST stay as CI does not have a GPU.
'--disable-gpu',
// Fully disable GPU process.
'--disable-software-rasterizer',
// Disable text subpixel antialiasing.
'--font-render-hinting=none',
'--disable-skia-runtime-opts',
'--disable-system-font-check',
'--disable-font-subpixel-positioning',
'--disable-lcd-text',
],
},
},
webServer: {
command: `corepack pnpm run ${process.env.CI || process.env.PROD ? 'dev:e2e:ci' : 'dev:e2e'}`,
port: 8080,
reuseExistingServer: false,
},
})

View File

@ -1,9 +0,0 @@
/** @file Configuration for PostCSS. */
/* eslint-disable no-restricted-syntax */
export default {
plugins: {
// eslint-disable-next-line @typescript-eslint/naming-convention
'tailwindcss/nesting': {},
tailwindcss: {},
},
}

View File

@ -1,8 +0,0 @@
/** @file Placeholder component for GUI used during e2e tests. */
import type * as editor from '#/layouts/Editor'
/** Placeholder component for GUI used during e2e tests. */
export function TestAppRunner(props: editor.GraphEditorProps) {
// eslint-disable-next-line no-restricted-syntax
return props.hidden ? <></> : <div data-testid="gui-editor-root">Vue app loads here.</div>
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -1,33 +0,0 @@
/** @file Entry point into the cloud dashboard. */
import * as commonQuery from 'enso-common/src/queryClient'
import '#/tailwind.css'
import * as main from '#/index'
import * as testAppRunner from '#/TestAppRunner'
// ===================
// === Entry point ===
// ===================
main.run({
logger: console,
// Browsers usually do not support vibrancy for webpages.
vibrancy: false,
// This file is only included when building for the cloud.
supportsLocalBackend: false,
supportsDeepLinks: false,
isAuthenticationDisabled: false,
shouldShowDashboard: true,
initialProjectName: null,
/** The `onAuthenticated` option is mandatory but is not needed here,
* so this function is empty. */
onAuthenticated() {
// eslint-disable-next-line @typescript-eslint/no-empty-function
},
/** The cloud frontend is not capable of running a Project Manager. */
projectManagerUrl: null,
ydocUrl: null,
appRunner: testAppRunner.TestAppRunner,
queryClient: commonQuery.createQueryClient(),
})

View File

@ -1,23 +0,0 @@
{
"extends": "../tsconfig.json",
"include": [
"src",
"e2e",
"../types",
"./src/**/*.json",
"./e2e/**/*.json",
"../../utils.ts",
".prettierrc.cjs",
"*.js",
"*.ts"
],
"exclude": ["./dist"],
"compilerOptions": {
"composite": true,
"noEmit": false,
"outDir": "../../node_modules/.cache/tsc",
"paths": { "#/*": ["./src/*"] },
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable", "ES2023"]
}
}

View File

@ -1,71 +0,0 @@
/** @file Configuration for vite. */
import * as fsSync from 'node:fs'
import * as url from 'node:url'
import vitePluginYaml from '@modyfi/vite-plugin-yaml'
import vitePluginReact from '@vitejs/plugin-react'
import * as vite from 'vite'
import * as common from 'enso-common'
import * as appConfig from 'enso-common/src/appConfig'
// =====================
// === Configuration ===
// =====================
const HTTP_STATUS_OK = 200
const SERVER_PORT = 8080
await appConfig.readEnvironmentFromFile()
/* eslint-disable @typescript-eslint/naming-convention */
export default vite.defineConfig({
server: { port: SERVER_PORT, headers: Object.fromEntries(common.COOP_COEP_CORP_HEADERS) },
plugins: [
vitePluginReact({
include: '**/*.tsx',
babel: { plugins: ['@babel/plugin-syntax-import-attributes'] },
}),
vitePluginYaml(),
serveFavicon(),
],
resolve: {
alias: {
'#': url.fileURLToPath(new URL('./src', import.meta.url)),
},
},
build: {
rollupOptions: {
input: {
main: url.fileURLToPath(new URL('./index.html', import.meta.url)),
'404': url.fileURLToPath(new URL('./404.html', import.meta.url)),
},
},
},
define: {
// The sole hardcoded usage of `global` in aws-amplify.
'global.TYPED_ARRAY_SUPPORT': JSON.stringify(true),
...appConfig.getDefines(),
},
})
/** A plugin to serve a favicon, in development mode only. */
function serveFavicon(): vite.Plugin {
const favicon = fsSync.readFileSync(url.fileURLToPath(new URL('./favicon.ico', import.meta.url)))
const headers: HeadersInit = [
['Content-Length', String(favicon.length)],
['Content-Type', 'image/png'],
...common.COOP_COEP_CORP_HEADERS,
]
return {
name: 'serve-favicon',
configureServer: (server) => {
server.middlewares.use((req, res, next) => {
if (req.url === '/favicon.ico') {
res.writeHead(HTTP_STATUS_OK, headers).end(favicon)
} else {
next()
}
})
},
}
}

View File

@ -1,25 +0,0 @@
/** @file Configuration for vitest. */
import * as url from 'node:url'
import * as vitestConfig from 'vitest/config'
import * as appConfig from 'enso-common/src/appConfig'
appConfig.loadTestEnvironmentVariables()
// @ts-expect-error This is required, otherwise importing node modules is broken.
// This is required for `datalinkSchema.test.ts`.
process.env.NODE_ENV = 'development'
const VITE_CONFIG = (await import('./vite.config')).default
export default vitestConfig.mergeConfig(
VITE_CONFIG,
vitestConfig.defineConfig({
test: {
environment: 'jsdom',
exclude: [...vitestConfig.configDefaults.exclude, '**/*.spec.{ts,tsx}'],
root: url.fileURLToPath(new URL('./', import.meta.url)),
restoreMocks: true,
},
}),
)

View File

@ -8,6 +8,7 @@ node_modules
dist dist
dist-ssr dist-ssr
coverage coverage
mockDist
*.local *.local
*.tsbuildinfo *.tsbuildinfo
@ -25,12 +26,11 @@ coverage
test-results/ test-results/
playwright-report/ playwright-report/
src/util/iconList.json
src/util/iconName.ts
src/stores/visualization/metadata.json
public/font-dejavu/ public/font-dejavu/
public/font-enso/ public/font-enso/
public/font-mplus1/ public/font-mplus1/
src/assets/font-dejavu.css src/project-view/assets/font-dejavu.css
src/assets/font-enso.css src/project-view/assets/font-enso.css
src/assets/font-mplus1.css src/project-view/assets/font-mplus1.css
src/project-view/util/iconList.json
src/project-view/util/iconName.ts

View File

@ -3,7 +3,7 @@ import * as test from '@playwright/test'
import type * as inputBindings from '#/utilities/inputBindings' import type * as inputBindings from '#/utilities/inputBindings'
import { modModifier } from '../actions' import { modModifier } from '.'
// ==================== // ====================
// === PageCallback === // === PageCallback ===
@ -27,7 +27,8 @@ export interface LocatorCallback {
// === BaseActions === // === BaseActions ===
// =================== // ===================
/** The base class from which all `Actions` classes are derived. /**
* The base class from which all `Actions` classes are derived.
* It contains method common to all `Actions` subclasses. * It contains method common to all `Actions` subclasses.
* This is a [`thenable`], so it can be used as if it was a {@link Promise}. * This is a [`thenable`], so it can be used as if it was a {@link Promise}.
* *
@ -40,14 +41,18 @@ export default class BaseActions implements Promise<void> {
private readonly promise = Promise.resolve(), private readonly promise = Promise.resolve(),
) {} ) {}
/** Get the string name of the class of this instance. Required for this class to implement /**
* {@link Promise}. */ * Get the string name of the class of this instance. Required for this class to implement
* {@link Promise}.
*/
get [Symbol.toStringTag]() { get [Symbol.toStringTag]() {
return this.constructor.name return this.constructor.name
} }
/** Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control` /**
* on all other platforms. */ * Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control`
* on all other platforms.
*/
static press(page: test.Page, keyOrShortcut: string): Promise<void> { static press(page: test.Page, keyOrShortcut: string): Promise<void> {
return test.test.step(`Press '${keyOrShortcut}'`, async () => { return test.test.step(`Press '${keyOrShortcut}'`, async () => {
if (/\bMod\b|\bDelete\b/.test(keyOrShortcut)) { if (/\bMod\b|\bDelete\b/.test(keyOrShortcut)) {
@ -77,18 +82,22 @@ export default class BaseActions implements Promise<void> {
return await this.promise.then(onfulfilled, onrejected) return await this.promise.then(onfulfilled, onrejected)
} }
/** Proxies the `catch` method of the internal {@link Promise}. /**
* Proxies the `catch` method of the internal {@link Promise}.
* This method is not required for this to be a `thenable`, but it is still useful * This method is not required for this to be a `thenable`, but it is still useful
* to treat this class as a {@link Promise}. */ * to treat this class as a {@link Promise}.
*/
// The following types are copied almost verbatim from the type definitions for `Promise`. // The following types are copied almost verbatim from the type definitions for `Promise`.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
async catch<T>(onrejected?: ((reason: unknown) => PromiseLike<T> | T) | null | undefined) { async catch<T>(onrejected?: ((reason: unknown) => PromiseLike<T> | T) | null | undefined) {
return await this.promise.catch(onrejected) return await this.promise.catch(onrejected)
} }
/** Proxies the `catch` method of the internal {@link Promise}. /**
* Proxies the `catch` method of the internal {@link Promise}.
* This method is not required for this to be a `thenable`, but it is still useful * This method is not required for this to be a `thenable`, but it is still useful
* to treat this class as a {@link Promise}. */ * to treat this class as a {@link Promise}.
*/
async finally(onfinally?: (() => void) | null | undefined): Promise<void> { async finally(onfinally?: (() => void) | null | undefined): Promise<void> {
await this.promise.finally(onfinally) await this.promise.finally(onfinally)
} }
@ -101,9 +110,11 @@ export default class BaseActions implements Promise<void> {
return new clazz(this.page, this.promise, ...args) return new clazz(this.page, this.promise, ...args)
} }
/** Perform an action on the current page. This should generally be avoided in favor of using /**
* Perform an action on the current page. This should generally be avoided in favor of using
* specific methods; this is more or less an escape hatch used ONLY when the methods do not * specific methods; this is more or less an escape hatch used ONLY when the methods do not
* support desired functionality. */ * support desired functionality.
*/
do(callback: PageCallback): this { do(callback: PageCallback): this {
// @ts-expect-error This is SAFE, but only when the constructor of this class has the exact // @ts-expect-error This is SAFE, but only when the constructor of this class has the exact
// same parameters as `BaseActions`. // same parameters as `BaseActions`.
@ -119,8 +130,10 @@ export default class BaseActions implements Promise<void> {
return this.do(() => test.test.step(name, () => callback(this.page))) return this.do(() => test.test.step(name, () => callback(this.page)))
} }
/** Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control` /**
* on all other platforms. */ * Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control`
* on all other platforms.
*/
press<Key extends string>(keyOrShortcut: inputBindings.AutocompleteKeybind<Key>) { press<Key extends string>(keyOrShortcut: inputBindings.AutocompleteKeybind<Key>) {
return this.do((page) => BaseActions.press(page, keyOrShortcut)) return this.do((page) => BaseActions.press(page, keyOrShortcut))
} }
@ -157,8 +170,10 @@ export default class BaseActions implements Promise<void> {
}) })
} }
/** Expect an input to have an error (or no error if the expected value is `null`). /**
* If the expected value is `undefined`, the assertion is skipped. */ * Expect an input to have an error (or no error if the expected value is `null`).
* If the expected value is `undefined`, the assertion is skipped.
*/
expectInputError(testId: string, description: string, expected: string | null | undefined) { expectInputError(testId: string, description: string, expected: string | null | undefined) {
if (expected === undefined) { if (expected === undefined) {
return this return this

View File

@ -12,7 +12,7 @@ import {
locateSecretNameInput, locateSecretNameInput,
locateSecretValueInput, locateSecretValueInput,
TEXT, TEXT,
} from '../actions' } from '.'
import type * as baseActions from './BaseActions' import type * as baseActions from './BaseActions'
import * as contextMenuActions from './contextMenuActions' import * as contextMenuActions from './contextMenuActions'
import EditorPageActions from './EditorPageActions' import EditorPageActions from './EditorPageActions'
@ -120,8 +120,10 @@ export default class DrivePageActions extends PageActions {
locateAssetRows(page).nth(index).click({ position: ASSET_ROW_SAFE_POSITION }), locateAssetRows(page).nth(index).click({ position: ASSET_ROW_SAFE_POSITION }),
) )
}, },
/** Right click a specific row to bring up its context menu, or the context menu for multiple /**
* assets when right clicking on a selected asset when multiple assets are selected. */ * Right click a specific row to bring up its context menu, or the context menu for multiple
* assets when right clicking on a selected asset when multiple assets are selected.
*/
rightClickRow(index: number) { rightClickRow(index: number) {
return self.step(`Right click drive table row #${index}`, (page) => return self.step(`Right click drive table row #${index}`, (page) =>
locateAssetRows(page) locateAssetRows(page)
@ -164,8 +166,10 @@ export default class DrivePageActions extends PageActions {
}), }),
) )
}, },
/** A test assertion to confirm that there is only one row visible, and that row is the /**
* placeholder row displayed when there are no assets to show. */ * A test assertion to confirm that there is only one row visible, and that row is the
* placeholder row displayed when there are no assets to show.
*/
expectPlaceholderRow() { expectPlaceholderRow() {
return self.step('Expect placeholder row', async (page) => { return self.step('Expect placeholder row', async (page) => {
await test.expect(locateAssetRows(page)).toHaveCount(0) await test.expect(locateAssetRows(page)).toHaveCount(0)
@ -174,8 +178,10 @@ export default class DrivePageActions extends PageActions {
await test.expect(nonAssetRows).toHaveText(/This folder is empty/) await test.expect(nonAssetRows).toHaveText(/This folder is empty/)
}) })
}, },
/** A test assertion to confirm that there is only one row visible, and that row is the /**
* placeholder row displayed when there are no assets in Trash. */ * A test assertion to confirm that there is only one row visible, and that row is the
* placeholder row displayed when there are no assets in Trash.
*/
expectTrashPlaceholderRow() { expectTrashPlaceholderRow() {
return self.step('Expect trash placeholder row', async (page) => { return self.step('Expect trash placeholder row', async (page) => {
await test.expect(locateAssetRows(page)).toHaveCount(0) await test.expect(locateAssetRows(page)).toHaveCount(0)

View File

@ -1,7 +1,7 @@
/** @file Available actions for the login page. */ /** @file Available actions for the login page. */
import * as test from '@playwright/test' import * as test from '@playwright/test'
import { TEXT, VALID_EMAIL } from '../actions' import { TEXT, VALID_EMAIL } from '.'
import BaseActions, { type LocatorCallback } from './BaseActions' import BaseActions, { type LocatorCallback } from './BaseActions'
import LoginPageActions from './LoginPageActions' import LoginPageActions from './LoginPageActions'

View File

@ -1,7 +1,7 @@
/** @file Available actions for the login page. */ /** @file Available actions for the login page. */
import * as test from '@playwright/test' import * as test from '@playwright/test'
import { TEXT, VALID_EMAIL, VALID_PASSWORD, passAgreementsDialog } from '../actions' import { TEXT, VALID_EMAIL, VALID_PASSWORD, passAgreementsDialog } from '.'
import BaseActions, { type LocatorCallback } from './BaseActions' import BaseActions, { type LocatorCallback } from './BaseActions'
import DrivePageActions from './DrivePageActions' import DrivePageActions from './DrivePageActions'
import ForgotPasswordPageActions from './ForgotPasswordPageActions' import ForgotPasswordPageActions from './ForgotPasswordPageActions'

View File

@ -1,7 +1,7 @@
/** @file Actions for a "new Data Link" modal. */ /** @file Actions for a "new Data Link" modal. */
import type * as test from 'playwright/test' import type * as test from 'playwright/test'
import { TEXT } from '../actions' import { TEXT } from '.'
import type * as baseActions from './BaseActions' import type * as baseActions from './BaseActions'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import DrivePageActions from './DrivePageActions' import DrivePageActions from './DrivePageActions'

View File

@ -1,7 +1,7 @@
/** @file Available actions for the login page. */ /** @file Available actions for the login page. */
import * as test from '@playwright/test' import * as test from '@playwright/test'
import { TEXT, VALID_EMAIL, VALID_PASSWORD } from '../actions' import { TEXT, VALID_EMAIL, VALID_PASSWORD } from '.'
import BaseActions, { type LocatorCallback } from './BaseActions' import BaseActions, { type LocatorCallback } from './BaseActions'
import LoginPageActions from './LoginPageActions' import LoginPageActions from './LoginPageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the fourth step of the "setup" page. */ /** @file Actions for the fourth step of the "setup" page. */
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import DrivePageActions from './DrivePageActions' import DrivePageActions from './DrivePageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the third step of the "setup" page. */ /** @file Actions for the third step of the "setup" page. */
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import SetupTeamPageActions from './SetupTeamPageActions' import SetupTeamPageActions from './SetupTeamPageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the third step of the "setup" page. */ /** @file Actions for the third step of the "setup" page. */
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import SetupInvitePageActions from './SetupInvitePageActions' import SetupInvitePageActions from './SetupInvitePageActions'

View File

@ -1,7 +1,7 @@
/** @file Actions for the second step of the "setup" page. */ /** @file Actions for the second step of the "setup" page. */
import { PLAN_TO_UPGRADE_LABEL_ID } from '#/modules/payments/constants' import { PLAN_TO_UPGRADE_LABEL_ID } from '#/modules/payments/constants'
import { Plan } from 'enso-common/src/services/Backend' import { Plan } from 'enso-common/src/services/Backend'
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import SetupDonePageActions from './SetupDonePageActions' import SetupDonePageActions from './SetupDonePageActions'
import SetupOrganizationPageActions from './SetupOrganizationPageActions' import SetupOrganizationPageActions from './SetupOrganizationPageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the "setup" page. */ /** @file Actions for the "setup" page. */
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import SetupDonePageActions from './SetupDonePageActions' import SetupDonePageActions from './SetupDonePageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the "setup" page. */ /** @file Actions for the "setup" page. */
import { TEXT } from '../actions' import { TEXT } from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import SetupPlanPageActions from './SetupPlanPageActions' import SetupPlanPageActions from './SetupPlanPageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the "home" page. */ /** @file Actions for the "home" page. */
import * as actions from '../actions' import * as actions from '.'
import BaseActions from './BaseActions' import BaseActions from './BaseActions'
import DrivePageActions from './DrivePageActions' import DrivePageActions from './DrivePageActions'
import EditorPageActions from './EditorPageActions' import EditorPageActions from './EditorPageActions'

View File

@ -1,5 +1,5 @@
/** @file Actions for the context menu. */ /** @file Actions for the context menu. */
import { TEXT } from '../actions' import { TEXT } from '.'
import type * as baseActions from './BaseActions' import type * as baseActions from './BaseActions'
import type BaseActions from './BaseActions' import type BaseActions from './BaseActions'
import EditorPageActions from './EditorPageActions' import EditorPageActions from './EditorPageActions'

View File

@ -4,9 +4,9 @@ import * as test from '@playwright/test'
import { TEXTS } from 'enso-common/src/text' import { TEXTS } from 'enso-common/src/text'
import DrivePageActions from './actions/DrivePageActions' import * as apiModule from '../api'
import LoginPageActions from './actions/LoginPageActions' import DrivePageActions from './DrivePageActions'
import * as apiModule from './api' import LoginPageActions from './LoginPageActions'
/* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-namespace */
@ -292,7 +292,7 @@ export function locateSamples(page: test.Locator | test.Page) {
/** Find an editor container (if any) on the current page. */ /** Find an editor container (if any) on the current page. */
export function locateEditor(page: test.Page) { export function locateEditor(page: test.Page) {
// Test ID of a placeholder editor component used during testing. // Test ID of a placeholder editor component used during testing.
return page.getByTestId('gui-editor-root') return page.locator('.App')
} }
/** Find an assets table (if any) on the current page. */ /** Find an assets table (if any) on the current page. */
@ -315,16 +315,20 @@ export function locateAssetName(locator: test.Locator) {
return locator.locator('> :nth-child(1)') return locator.locator('> :nth-child(1)')
} }
/** Find assets table rows that represent directories that can be expanded (if any) /**
* on the current page. */ * Find assets table rows that represent directories that can be expanded (if any)
* on the current page.
*/
export function locateExpandableDirectories(page: test.Page) { export function locateExpandableDirectories(page: test.Page) {
// The icon is hidden when not hovered so `getByLabel` will not work. // The icon is hidden when not hovered so `getByLabel` will not work.
// eslint-disable-next-line no-restricted-properties // eslint-disable-next-line no-restricted-properties
return locateAssetRows(page).filter({ has: page.locator('[aria-label=Expand]') }) return locateAssetRows(page).filter({ has: page.locator('[aria-label=Expand]') })
} }
/** Find assets table rows that represent directories that can be collapsed (if any) /**
* on the current page. */ * Find assets table rows that represent directories that can be collapsed (if any)
* on the current page.
*/
export function locateCollapsibleDirectories(page: test.Page) { export function locateCollapsibleDirectories(page: test.Page) {
// The icon is hidden when not hovered so `getByLabel` will not work. // The icon is hidden when not hovered so `getByLabel` will not work.
// eslint-disable-next-line no-restricted-properties // eslint-disable-next-line no-restricted-properties
@ -390,9 +394,11 @@ export function locateExtraColumns(page: test.Page) {
return page.getByTestId('extra-columns') return page.getByTestId('extra-columns')
} }
/** Find a root directory dropzone (if any) on the current page. /**
* Find a root directory dropzone (if any) on the current page.
* This is the empty space below the assets table, if it doesn't take up the whole screen * This is the empty space below the assets table, if it doesn't take up the whole screen
* vertically. */ * vertically.
*/
export function locateRootDirectoryDropzone(page: test.Page) { export function locateRootDirectoryDropzone(page: test.Page) {
// This has no identifying features. // This has no identifying features.
return page.getByTestId('root-directory-dropzone') return page.getByTestId('root-directory-dropzone')
@ -591,9 +597,11 @@ export namespace settings {
// === Visual layout utilities === // === Visual layout utilities ===
// =============================== // ===============================
/** Get the left side of the bounding box of an asset row. The locator MUST be for an asset row. /**
* Get the left side of the bounding box of an asset row. The locator MUST be for an asset row.
* DO NOT assume the left side of the outer container will change. This means that it is NOT SAFE * DO NOT assume the left side of the outer container will change. This means that it is NOT SAFE
* to do anything with the returned values other than comparing them. */ * to do anything with the returned values other than comparing them.
*/
export function getAssetRowLeftPx(locator: test.Locator) { export function getAssetRowLeftPx(locator: test.Locator) {
return locator.evaluate((el) => el.children[0]?.children[0]?.getBoundingClientRect().left ?? 0) return locator.evaluate((el) => el.children[0]?.children[0]?.getBoundingClientRect().left ?? 0)
} }
@ -688,8 +696,10 @@ export async function modModifier(page: test.Page) {
return /\bMac OS\b/i.test(userAgent) ? 'Meta' : 'Control' return /\bMac OS\b/i.test(userAgent) ? 'Meta' : 'Control'
} }
/** Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control` /**
* on all other platforms. */ * Press a key, replacing the text `Mod` with `Meta` (`Cmd`) on macOS, and `Control`
* on all other platforms.
*/
export async function press(page: test.Page, keyOrShortcut: string) { export async function press(page: test.Page, keyOrShortcut: string) {
await test.test.step(`Press '${keyOrShortcut}'`, async () => { await test.test.step(`Press '${keyOrShortcut}'`, async () => {
if (/\bMod\b|\bDelete\b/.test(keyOrShortcut)) { if (/\bMod\b|\bDelete\b/.test(keyOrShortcut)) {
@ -837,8 +847,10 @@ export function mockAllAndLogin({ page, setupAPI }: MockParams) {
.do((thePage) => login({ page: thePage, setupAPI })) .do((thePage) => login({ page: thePage, setupAPI }))
} }
/** Set up all mocks, and log in with dummy credentials. /**
* @deprecated Prefer {@link mockAllAndLogin}. */ * Set up all mocks, and log in with dummy credentials.
* @deprecated Prefer {@link mockAllAndLogin}.
*/
// This syntax is required for Playwright to work properly. // This syntax is required for Playwright to work properly.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
export async function mockAllAndLoginAndExposeAPI({ page, setupAPI }: MockParams) { export async function mockAllAndLoginAndExposeAPI({ page, setupAPI }: MockParams) {

View File

@ -1,5 +1,5 @@
/** @file An action to open the User Menu. */ /** @file An action to open the User Menu. */
import { TEXT } from '../actions' import { TEXT } from '.'
import type BaseActions from './BaseActions' import type BaseActions from './BaseActions'
import type { PageCallback } from './BaseActions' import type { PageCallback } from './BaseActions'

View File

@ -58,7 +58,7 @@ export interface SetupAPI {
} }
/** The return type of {@link mockApi}. */ /** The return type of {@link mockApi}. */
export interface MockApi extends Awaited<ReturnType<typeof mockApiInternal>> {} export type MockApi = Awaited<ReturnType<typeof mockApiInternal>>
// This is a function, even though it does not contain function syntax. // This is a function, even though it does not contain function syntax.
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
@ -582,7 +582,7 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
'Development' satisfies `${backend.VersionLifecycle.development}` as backend.VersionLifecycle.development, 'Development' satisfies `${backend.VersionLifecycle.development}` as backend.VersionLifecycle.development,
value: '2023.2.1-dev', value: '2023.2.1-dev',
}, },
// eslint-disable-next-line @typescript-eslint/naming-convention, no-restricted-syntax // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase, no-restricted-syntax
version_type: (new URL(request.url()).searchParams.get('version_type') ?? version_type: (new URL(request.url()).searchParams.get('version_type') ??
'') as backend.VersionType, '') as backend.VersionType,
} satisfies backend.Version, } satisfies backend.Version,
@ -603,9 +603,9 @@ async function mockApiInternal({ page, setupAPI }: MockParams) {
name: 'example project name', name: 'example project name',
state: project.projectState, state: project.projectState,
packageName: 'Project_root', packageName: 'Project_root',
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase
ide_version: null, ide_version: null,
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase
engine_version: { engine_version: {
value: '2023.2.1-nightly.2023.9.29', value: '2023.2.1-nightly.2023.9.29',
lifecycle: backend.VersionLifecycle.development, lifecycle: backend.VersionLifecycle.development,

View File

@ -9,12 +9,7 @@ import type {
import { createContext, useContext, useEffect, useState } from 'react' import { createContext, useContext, useEffect, useState } from 'react'
/** */ /** */
type ElementsContextValue_ = Parameters<Parameters<typeof StripeElementConsumer>[0]['children']>[0] type ElementsContextValue = Parameters<Parameters<typeof StripeElementConsumer>[0]['children']>[0]
/** */
interface ElementsContextValue extends ElementsContextValue_ {
//
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const ElementsContext = createContext<ElementsContextValue>(null!) const ElementsContext = createContext<ElementsContextValue>(null!)

View File

@ -10,7 +10,7 @@ export const loadStripe = (): Promise<Stripe> =>
paymentMethod: { paymentMethod: {
id: '', id: '',
object: 'payment_method', object: 'payment_method',
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention, camelcase
billing_details: { billing_details: {
address: null, address: null,
email: null, email: null,

View File

@ -46,7 +46,7 @@ test('Using breadcrumbs to navigate', async ({ page }) => {
await expectInsideMain(page) await expectInsideMain(page)
// Breadcrumbs still have all the crumbs, but the last two are dimmed. // Breadcrumbs still have all the crumbs, but the last two are dimmed.
await expect(locate.navBreadcrumb(page)).toHaveText(['Mock Project', 'func1', 'func2']) await expect(locate.navBreadcrumb(page)).toHaveText(['Mock Project', 'func1', 'func2'])
await expect(locate.navBreadcrumb(page, (f) => f.class('inactive'))).toHaveText([ await expect(locate.navBreadcrumb(page).and(page.locator('.inactive'))).toHaveText([
'func1', 'func1',
'func2', 'func2',
]) ])

Some files were not shown because too many files have changed in this diff Show More