diff --git a/.env.example b/.env.example
new file mode 100644
index 000000000..b5ad063eb
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,59 @@
+# Rename this file to `.env`
+
+NODE_ENV=development
+
+# Companion
+# =======================
+COMPANION_DATADIR=./output
+COMPANION_DOMAIN=localhost:3020
+COMPANION_PROTOCOL=http
+COMPANION_PORT=3020
+COMPANION_CLIENT_ORIGINS=
+COMPANION_SECRET=development
+
+COMPANION_DROPBOX_KEY=***
+COMPANION_DROPBOX_SECRET=***
+
+COMPANION_BOX_KEY=***
+COMPANION_BOX_SECRET=***
+
+COMPANION_GOOGLE_KEY=***
+COMPANION_GOOGLE_SECRET=***
+
+COMPANION_INSTAGRAM_KEY=***
+COMPANION_INSTAGRAM_SECRET=***
+
+COMPANION_FACEBOOK_KEY=***
+COMPANION_FACEBOOK_SECRET=***
+
+COMPANION_ZOOM_KEY=***
+COMPANION_ZOOM_SECRET=***
+
+COMPANION_UNSPLASH_KEY=***
+COMPANION_UNSPLASH_SECRET=***
+
+# Transloadit keys can also be used in `e2e/clients`
+TRANSLOADIT_KEY=***
+TRANSLOADIT_TEMPLATE=***
+TRANSLOADIT_SERVICE_URL=https://api2.transloadit.com
+
+# Development environment
+# =======================
+
+VITE_UPLOADER=tus
+# VITE_UPLOADER=s3
+# VITE_UPLOADER=s3-multipart
+# xhr will use protocol 'multipart' in companion, if used with a remote service, e.g. google drive.
+# If local upload will use browser XHR
+# VITE_UPLOADER=xhr
+# VITE_UPLOADER=transloadit
+# VITE_UPLOADER=transloadit-s3
+# VITE_UPLOADER=transloadit-xhr
+
+VITE_COMPANION_URL=http://localhost:3020
+VITE_TUS_ENDPOINT=https://tusd.tusdemo.net/files/
+VITE_XHR_ENDPOINT=https://xhr-server.herokuapp.com/upload
+
+VITE_TRANSLOADIT_KEY=***
+VITE_TRANSLOADIT_TEMPLATE=***
+VITE_TRANSLOADIT_SERVICE_URL=https://api2.transloadit.com
diff --git a/.eslintrc.js b/.eslintrc.js
index b4a2743cb..fd86afb0c 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -291,9 +291,14 @@ module.exports = {
files: ['**/*.ts', '**/*.md/*.ts', '**/*.md/*.typescript'],
excludedFiles: ['examples/angular-example/**/*.ts', 'packages/@uppy/angular/**/*.ts'],
parser: '@typescript-eslint/parser',
- plugins: [
- '@typescript-eslint',
- ],
+ settings: {
+ 'import/resolver': {
+ node: {
+ extensions: ['.js', '.jsx', '.ts', '.tsx'],
+ },
+ },
+ },
+ plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
@@ -302,6 +307,7 @@ module.exports = {
rules: {
'import/prefer-default-export': 'off',
'@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-namespace': 'off',
},
},
{
@@ -320,5 +326,13 @@ module.exports = {
react: { pragma: 'React' },
},
},
+ {
+ files: ['e2e/**/*.ts'],
+ extends: ['plugin:cypress/recommended'],
+ },
+ {
+ files: ['e2e/**/*.ts', 'e2e/**/*.js'],
+ rules: { 'import/no-extraneous-dependencies': 'off' },
+ },
],
}
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 861444e2b..82f1e167f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -30,52 +30,27 @@ Our website’s examples section is also our playground, please read the [Local
## Tests
+### Unit tests
+
Unit tests are using Jest and can be run with:
```bash
yarn run test:unit
```
-For end-to-end tests, we use [Webdriverio](http://webdriver.io). For it to run locally, you need to install a Selenium standalone server. Follow [the Webdriverio guide](https://webdriver.io/docs/selenium-standalone-service) to do so. You can also install a Selenium standalone server from NPM:
+### End-to-End tests
-```bash
-npm install selenium-standalone -g
-selenium-standalone install
-```
+We use [Cypress](cypress.io/) for our e2e test suite. Be sure to checkout “[Writing your first test](https://docs.cypress.io/guides/getting-started/writing-your-first-test#Add-a-test-file)” and the “[Introduction to Cypress](https://docs.cypress.io/guides/core-concepts/introduction-to-cypress#Cypress-Can-Be-Simple-Sometimes)”. You should also be aware of the “[Best Practices](https://docs.cypress.io/guides/references/best-practices)”.
-And then launch it:
+To get started make sure you have your `.env` set up. If it doesn’t exist, copy and rename the `.env.local` file and populate (some) of the values. What values need to be populated depends on what tests you want to run.
-```bash
-selenium-standalone start
-```
+To start the testing suite run:
-After you have installed and launched the selenium standalone server, run:
+ yarn e2e
-```bash
-yarn run test:endtoend:local
-```
+To generate the boilerplate for a new test run:
-By default, `test:endtoend:local` uses Firefox. You can use a different browser, like Chrome, by passing the `-b` flag:
-
-```bash
-yarn run test:endtoend:local -- -b chrome
-```
-
-> Note: The `--` is important, it tells yarn that the remaining arguments should be interpreted by the script itself, not by yarn.
-
-You can run in several browsers by passing several `-b` flags:
-
-```bash
-yarn run test:endtoend:local -- -b chrome -b firefox
-```
-
-When trying to get a specific integration test to pass, it’s not that helpful to continuously run _all_ tests. You can use the `--suite` flag to run tests from a single `./test/endtoend` folder. For example, `--suite thumbnails` will only run the tests from `./test/endtoend/thumbnails`. It can also be used in conjunction with one or more `-b` flags.
-
-```bash
-yarn run test:endtoend:local -- -b chrome --suite thumbnails
-```
-
-These tests are also run automatically on Travis builds with [SauceLabs](https://saucelabs.com/) cloud service using different OSes.
+ yarn e2e:generate
## Development
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 97dc1726d..810388aca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,11 @@
name: CI
-on: [push, pull_request]
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # We want all branches so we configure types to be the GH default again
+ types: [ opened, synchronize, reopened ]
jobs:
unit_tests:
diff --git a/.github/workflows/companion.yml b/.github/workflows/companion.yml
index 7c85fcfca..1472d57ce 100644
--- a/.github/workflows/companion.yml
+++ b/.github/workflows/companion.yml
@@ -1,5 +1,10 @@
name: Companion
-on: [push, pull_request]
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # We want all branches so we configure types to be the GH default again
+ types: [ opened, synchronize, reopened ]
jobs:
test:
diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml
index 56d081d28..24d78e8df 100644
--- a/.github/workflows/end-to-end.yml
+++ b/.github/workflows/end-to-end.yml
@@ -1,11 +1,10 @@
name: End-to-end tests
-on: push
-
-env:
- GIT_AUTHOR_NAME: Uppy Bot
- GIT_COMMITTER_NAME: Uppy Bot
- GIT_AUTHOR_EMAIL: uppybot@uppy.io
- GIT_COMMITTER_EMAIL: uppybot@uppy.io
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ # We want all branches so we configure types to be the GH default again
+ types: [ opened, synchronize, reopened ]
jobs:
e2e:
@@ -30,17 +29,13 @@ jobs:
node-version: 16.x
- name: Install dependencies
run: corepack yarn install --immutable
- - name: Start Sauce Connect
- uses: saucelabs/sauce-connect-action@v1.1.1
- with:
- username: ${{secrets.SAUCE_USERNAME}}
- accessKey: ${{secrets.SAUCE_ACCESS_KEY}}
- tunnelIdentifier: ${{github.run_id}}.${{github.run_number}}
+ - name: Build Uppy packages
+ run: corepack yarn build
- name: Run end-to-end browser tests
- run: corepack yarn run test:endtoend
+ run: corepack yarn run e2e:ci
env:
- SAUCE_TUNNEL_IDENTIFIER: ${{github.run_id}}.${{github.run_number}}
- SAUCE_BUILD: ${{github.run_id}}
- SAUCE_USERNAME: ${{secrets.SAUCE_USERNAME}}
- SAUCE_ACCESS_KEY: ${{secrets.SAUCE_ACCESS_KEY}}
- TRANSLOADIT_KEY: ${{secrets.TRANSLOADIT_KEY}}
+ COMPANION_UNSPLASH_KEY: ${{secrets.COMPANION_UNSPLASH_KEY}}
+ COMPANION_UNSPLASH_SECRET: ${{secrets.COMPANION_UNSPLASH_SECRET}}
+ VITE_TRANSLOADIT_KEY: ${{secrets.TRANSLOADIT_KEY}}
+ VITE_TRANSLOADIT_TEMPLATE: ${{secrets.TRANSLOADIT_TEMPLATE}}
+ VITE_TRANSLOADIT_SERVICE_URL: ${{secrets.TRANSLOADIT_SERVICE_URL}}
diff --git a/.gitignore b/.gitignore
index 922e834c2..980c94ea3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ node_modules
.yarn/install-state.gz
yarn-error.log
-env.sh
+.env
*.local
dist/
diff --git a/bin/companion b/bin/companion
deleted file mode 100755
index 11ea6d877..000000000
--- a/bin/companion
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/usr/bin/env bash
-. ./env.sh
-
-env \
- COMPANION_DATADIR="./output" \
- COMPANION_DOMAIN="localhost:3020" \
- COMPANION_PROTOCOL="http" \
- COMPANION_PORT=3020 \
- COMPANION_CLIENT_ORIGINS="" \
- COMPANION_SECRET="development" \
-nodemon --watch packages/@uppy/companion/src --exec node ./packages/@uppy/companion/src/standalone/start-server.js
diff --git a/bin/companion.sh b/bin/companion.sh
new file mode 100644
index 000000000..836cdbb4c
--- /dev/null
+++ b/bin/companion.sh
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+# Load local env vars. In CI, these are injected.
+if [ -f .env ]; then
+ # https://gist.github.com/mihow/9c7f559807069a03e302605691f85572?permalink_comment_id=3625310#gistcomment-3625310
+ set -a
+ source <(sed -e '/^#/d;/^\s*$/d' -e "s/'/'\\\''/g" -e "s/=\(.*\)/='\1'/g" .env)
+ set +a
+ nodemon --watch packages/@uppy/companion/src --exec node ./packages/@uppy/companion/src/standalone/start-server.js
+else
+ env \
+ COMPANION_DATADIR="./output" \
+ COMPANION_DOMAIN="localhost:3020" \
+ COMPANION_PROTOCOL="http" \
+ COMPANION_PORT=3020 \
+ COMPANION_CLIENT_ORIGINS="" \
+ COMPANION_SECRET="development" \
+ nodemon --watch packages/@uppy/companion/src --exec node ./packages/@uppy/companion/src/standalone/start-server.js
+fi
diff --git a/bin/endtoend-build b/bin/endtoend-build
deleted file mode 100755
index b5bc395c3..000000000
--- a/bin/endtoend-build
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env bash
-
-set -o pipefail
-set -o errexit
-set -o nounset
-set -o xtrace
-
-# Set magic variables for current file & dir
-__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
-__base="$(basename ${__file} .sh)"
-__root="$(cd "$(dirname "${__dir}")" && pwd)"
-
-echo "Preparing for end to end test: copying static HTML and CSS, building JS"
-rm -rf "${__root}/test/endtoend/node_modules"
-
-yarn run build
-
-bash "${__dir}/endtoend-build-tests"
diff --git a/bin/endtoend-build-ci b/bin/endtoend-build-ci
deleted file mode 100755
index 9a00ab926..000000000
--- a/bin/endtoend-build-ci
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/env bash
-
-set -o pipefail
-set -o errexit
-set -o nounset
-
-# Set magic variables for current file & dir
-__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
-__base="$(basename ${__file} .sh)"
-__root="$(cd "$(dirname "${__dir}")" && pwd)"
-
-if [ -z "${CI:-}" ]; then
- echo "!! Running CI-style end-to-end tests but CI environment variable is not set"
- echo "It is generally ill-advised to run the full end-to-end suite on your local machine."
- echo "You are probably looking for this instead:"
- echo " yarn run test:endtoend:local"
- echo ""
- echo "Hit Ctrl+C to stop or Enter if you REALLY DO want to run the full thing."
- read
-else
- echo ::group::endtoend_build_ci
-fi
-
-set -o xtrace
-
-YARN="corepack yarn"
-VERDACCIO_REGISTRY=http://localhost:4002
-CURRENT_COMMIT="$(git rev-parse HEAD)"
-
-cleanup() {
- rm -rf "${__root}/test/endtoend/node_modules"
- rm -rf "${__root}/test/endtoend/tmp"
- git reset
- git checkout $CURRENT_COMMIT
-
- if [ -n "${CI:-}" ]; then
- echo ::endgroup::
- fi
-}
-function on_exit() {
- # revert to public registry
- $YARN config unset npmScopes --home
- $YARN config unset npmRegistryServer --home
- $YARN config unset npmAuthToken --home
- $YARN config unset unsafeHttpWhitelist --home
- cleanup
-}
-trap on_exit EXIT
-
-echo "Preparing for end to end test: copying static HTML and CSS, building JS"
-rm -rf "${__root}/test/endtoend/node_modules"
-
-# list of @uppy/* packages
-PACKAGES="$($YARN workspaces list --json | node -e 'require("readline").createInterface({input:process.stdin}).on("line",l=>{const{name}=JSON.parse(l);if(name?.startsWith("@uppy/"))console.log(name)})')"
-
-cleanup
-# Initialise verdaccio storage path.
-mkdir -p "${__root}/test/endtoend/tmp/verdaccio"
-
-$YARN run build
-
-# https://github.com/facebook/create-react-app/pull/4626
-TOKEN="$($YARN npm-auth-to-token -u user -p password -e user@example.com -r "$VERDACCIO_REGISTRY")"
-
-git checkout -b endtoend-test-build
-# HACK this thing changes all the time for some reason on CI
-# so I'll just ignore it…
-git checkout -- yarn.lock
-
-$YARN config set npmRegistryServer "$VERDACCIO_REGISTRY" --home
-$YARN config set npmAuthToken "$TOKEN" --home
-$YARN config set npmScopes.uppy.npmRegistryServer "$VERDACCIO_REGISTRY" --home
-$YARN config set npmScopes.uppy.npmPublishRegistry "$VERDACCIO_REGISTRY" --home
-$YARN config set npmScopes.uppy.npmAuthToken "$TOKEN" --home
-$YARN config set unsafeHttpWhitelist "localhost" --home
-
-# Simulate a publish of everything, to the local registry,
-# without changing things in git
-ENDTOEND=1 $YARN workspaces foreach --include '@uppy/*'\
- version prerelease -d
-$YARN version apply --all --prerelease
-ENDTOEND=1 $YARN workspaces foreach --include '@uppy/*'\
- npm publish
-
-bash "${__dir}/endtoend-build-tests"
-
-cleanup
diff --git a/bin/endtoend-build-tests b/bin/endtoend-build-tests
deleted file mode 100755
index 62888c26d..000000000
--- a/bin/endtoend-build-tests
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env bash
-
-set -o pipefail
-set -o errexit
-set -o nounset
-set -o xtrace
-
-# Set magic variables for current file & dir
-__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
-__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
-__base="$(basename "${__file}" .sh)"
-__root="$(cd "$(dirname "${__dir}")" && pwd)"
-__e2eSuites="${__root}/test/endtoend"
-
-ESBUILD="$(corepack yarn workspace @uppy-tests/end2end bin esbuild)"
-
-for t in $(cd "$__e2eSuites" && ls -d */ | cut -f1 -d'/'); do
- if [ "$t" = "tmp" ]; then continue; fi
- if [ "$t" = "create-react-app" ]; then continue; fi
-
- mkdir -p "${__e2eSuites}/$t/dist"
- cp "${__root}/packages/uppy/dist/uppy.min.css" "${__e2eSuites}/$t/dist"
- cp "${__e2eSuites}/$t/index.html" "${__e2eSuites}/$t/dist"
-
- entryPointName="main.mjs"
- if [ "$t" = "typescript" ]; then entryPointName="main.ts"; fi
-
- "$ESBUILD" "${__e2eSuites}/$t/$entryPointName" \
- --bundle --target=es2017 \
- --outfile="${__e2eSuites}/$t/dist/bundle.js"
-done
-
-# Speeecial tests that need custom builds.
-pushd "${__e2eSuites}/create-react-app"
- npm install
- REACT_APP_ON_TRAVIS="${TRAVIS:-}" npm run build
-popd
diff --git a/e2e/.parcelrc b/e2e/.parcelrc
new file mode 100644
index 000000000..236c7c742
--- /dev/null
+++ b/e2e/.parcelrc
@@ -0,0 +1,9 @@
+{
+ "extends": "@parcel/config-default",
+ "transformers": {
+ "*.{js,mjs,jsx,cjs,ts,tsx}": [
+ "@parcel/transformer-js",
+ "@parcel/transformer-react-refresh-wrap"
+ ]
+ }
+}
diff --git a/e2e/clients/dashboard-react/App.js b/e2e/clients/dashboard-react/App.js
new file mode 100644
index 000000000..5dd8a680f
--- /dev/null
+++ b/e2e/clients/dashboard-react/App.js
@@ -0,0 +1,19 @@
+/* eslint-disable react/react-in-jsx-scope */
+import Uppy from '@uppy/core'
+// eslint-disable-next-line no-unused-vars
+import React from 'react'
+import { Dashboard } from '@uppy/react'
+
+import '@uppy/core/dist/style.css'
+import '@uppy/dashboard/dist/style.css'
+
+export default App
+
+function App () {
+ // Ideally we test with the `useUppy` hook,
+ // as that's how it should be used.
+ // But that results in breaking the rules of hooks errors?
+ const uppy = new Uppy()
+
+ return
+}
diff --git a/e2e/clients/dashboard-react/index.html b/e2e/clients/dashboard-react/index.html
new file mode 100644
index 000000000..e7c2d96c3
--- /dev/null
+++ b/e2e/clients/dashboard-react/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ dashboard-react
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-react/index.js b/e2e/clients/dashboard-react/index.js
new file mode 100644
index 000000000..199d63134
--- /dev/null
+++ b/e2e/clients/dashboard-react/index.js
@@ -0,0 +1,5 @@
+/* eslint-disable react/react-in-jsx-scope */
+import ReactDOM from 'react-dom'
+import App from './App'
+
+ReactDOM.render(, document.getElementById('app'))
diff --git a/e2e/clients/dashboard-transloadit/app.js b/e2e/clients/dashboard-transloadit/app.js
new file mode 100644
index 000000000..a47c82428
--- /dev/null
+++ b/e2e/clients/dashboard-transloadit/app.js
@@ -0,0 +1,22 @@
+import { Uppy } from '@uppy/core'
+import Dashboard from '@uppy/dashboard'
+import Transloadit from '@uppy/transloadit'
+
+import '@uppy/core/dist/style.css'
+import '@uppy/dashboard/dist/style.css'
+
+// Environment variables:
+// https://en.parceljs.org/env.html
+const uppy = new Uppy()
+ .use(Dashboard, { target: '#app', inline: true })
+ .use(Transloadit, {
+ service: process.env.VITE_TRANSLOADIT_SERVICE_URL,
+ waitForEncoding: true,
+ params: {
+ auth: { key: process.env.VITE_TRANSLOADIT_KEY },
+ template_id: process.env.VITE_TRANSLOADIT_TEMPLATE,
+ },
+ })
+
+// Keep this here to access uppy in tests
+window.uppy = uppy
diff --git a/e2e/clients/dashboard-transloadit/index.html b/e2e/clients/dashboard-transloadit/index.html
new file mode 100644
index 000000000..f36fb8dbb
--- /dev/null
+++ b/e2e/clients/dashboard-transloadit/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ dashboard-transloadit
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-tus/app.js b/e2e/clients/dashboard-tus/app.js
new file mode 100644
index 000000000..a7c131fef
--- /dev/null
+++ b/e2e/clients/dashboard-tus/app.js
@@ -0,0 +1,18 @@
+import { Uppy } from '@uppy/core'
+import Dashboard from '@uppy/dashboard'
+import Tus from '@uppy/tus'
+import Unsplash from '@uppy/unsplash'
+import Url from '@uppy/url'
+
+import '@uppy/core/dist/style.css'
+import '@uppy/dashboard/dist/style.css'
+
+const companionUrl = 'http://localhost:3020'
+const uppy = new Uppy()
+ .use(Dashboard, { target: '#app', inline: true })
+ .use(Tus, { endpoint: 'https://tusd.tusdemo.net/files' })
+ .use(Url, { target: Dashboard, companionUrl })
+ .use(Unsplash, { target: Dashboard, companionUrl })
+
+// Keep this here to access uppy in tests
+window.uppy = uppy
diff --git a/e2e/clients/dashboard-tus/index.html b/e2e/clients/dashboard-tus/index.html
new file mode 100644
index 000000000..9ee4db8d4
--- /dev/null
+++ b/e2e/clients/dashboard-tus/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ dashboard-tus
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-ui/app.js b/e2e/clients/dashboard-ui/app.js
new file mode 100644
index 000000000..93ac419d6
--- /dev/null
+++ b/e2e/clients/dashboard-ui/app.js
@@ -0,0 +1,13 @@
+import Uppy from '@uppy/core'
+import Dashboard from '@uppy/dashboard'
+import ImageEditor from '@uppy/image-editor'
+
+import '@uppy/core/dist/style.css'
+import '@uppy/dashboard/dist/style.css'
+
+const uppy = new Uppy()
+ .use(Dashboard, { target: '#app', inline: true })
+ .use(ImageEditor, { target: Dashboard })
+
+// Keep this here to access uppy in tests
+window.uppy = uppy
diff --git a/e2e/clients/dashboard-ui/index.html b/e2e/clients/dashboard-ui/index.html
new file mode 100644
index 000000000..e76c6332b
--- /dev/null
+++ b/e2e/clients/dashboard-ui/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ dashboard-ui
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-vue/App.vue b/e2e/clients/dashboard-vue/App.vue
new file mode 100644
index 000000000..3fa462b17
--- /dev/null
+++ b/e2e/clients/dashboard-vue/App.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-vue/index.html b/e2e/clients/dashboard-vue/index.html
new file mode 100644
index 000000000..248d7a040
--- /dev/null
+++ b/e2e/clients/dashboard-vue/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ dashboard-vue
+
+
+
+
+
+
diff --git a/e2e/clients/dashboard-vue/index.js b/e2e/clients/dashboard-vue/index.js
new file mode 100644
index 000000000..01433bca2
--- /dev/null
+++ b/e2e/clients/dashboard-vue/index.js
@@ -0,0 +1,4 @@
+import { createApp } from 'vue'
+import App from './App.vue'
+
+createApp(App).mount('#app')
diff --git a/e2e/clients/index.html b/e2e/clients/index.html
new file mode 100644
index 000000000..dbc800de3
--- /dev/null
+++ b/e2e/clients/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+ End-to-End test suite
+
+
+ Test apps
+