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

+