meta: Make E2E Great Again (#3444)
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
59
.env.example
Normal file
|
|
@ -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
|
||||
20
.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' },
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
|||
43
.github/CONTRIBUTING.md
vendored
|
|
@ -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
|
||||
|
||||
|
|
|
|||
8
.github/workflows/ci.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
7
.github/workflows/companion.yml
vendored
|
|
@ -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:
|
||||
|
|
|
|||
33
.github/workflows/end-to-end.yml
vendored
|
|
@ -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}}
|
||||
|
|
|
|||
2
.gitignore
vendored
|
|
@ -13,7 +13,7 @@ node_modules
|
|||
.yarn/install-state.gz
|
||||
yarn-error.log
|
||||
|
||||
env.sh
|
||||
.env
|
||||
*.local
|
||||
|
||||
dist/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
19
bin/companion.sh
Normal file
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
9
e2e/.parcelrc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "@parcel/config-default",
|
||||
"transformers": {
|
||||
"*.{js,mjs,jsx,cjs,ts,tsx}": [
|
||||
"@parcel/transformer-js",
|
||||
"@parcel/transformer-react-refresh-wrap"
|
||||
]
|
||||
}
|
||||
}
|
||||
19
e2e/clients/dashboard-react/App.js
Normal file
|
|
@ -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 <Dashboard uppy={uppy} />
|
||||
}
|
||||
11
e2e/clients/dashboard-react/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dashboard-react</title>
|
||||
<script defer type="module" src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
5
e2e/clients/dashboard-react/index.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/* eslint-disable react/react-in-jsx-scope */
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('app'))
|
||||
22
e2e/clients/dashboard-transloadit/app.js
Normal file
|
|
@ -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
|
||||
11
e2e/clients/dashboard-transloadit/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dashboard-transloadit</title>
|
||||
<script defer type="module" src="app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
18
e2e/clients/dashboard-tus/app.js
Normal file
|
|
@ -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
|
||||
11
e2e/clients/dashboard-tus/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dashboard-tus</title>
|
||||
<script defer type="module" src="app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
13
e2e/clients/dashboard-ui/app.js
Normal file
|
|
@ -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
|
||||
11
e2e/clients/dashboard-ui/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dashboard-ui</title>
|
||||
<script defer type="module" src="app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
22
e2e/clients/dashboard-vue/App.vue
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<template>
|
||||
<dashboard :uppy="uppy" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import Uppy from '@uppy/core'
|
||||
import { Dashboard } from '@uppy/vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
Dashboard,
|
||||
},
|
||||
computed: {
|
||||
uppy: () => new Uppy()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style src='@uppy/core/dist/style.css'></style>
|
||||
<style src='@uppy/dashboard/dist/style.css'></style>
|
||||
11
e2e/clients/dashboard-vue/index.html
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dashboard-vue</title>
|
||||
<script defer type="module" src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
4
e2e/clients/dashboard-vue/index.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
19
e2e/clients/index.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>End-to-End test suite</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test apps</h1>
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="dashboard-tus/index.html">dashboard-tus</a></li>
|
||||
<li><a href="dashboard-transloadit/index.html">dashboard-transloadit</a></li>
|
||||
<li><a href="dashboard-ui/index.html">dashboard-ui</a></li>
|
||||
<li><a href="dashboard-react/index.html">dashboard-react</a></li>
|
||||
<li><a href="dashboard-vue/index.html">dashboard-vue</a></li>
|
||||
</ul>
|
||||
<nav>
|
||||
</body>
|
||||
</html>
|
||||
4
e2e/cypress.json
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"baseUrl": "http://localhost:1234",
|
||||
"screenshotOnRunFailure": false
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const deepFreeze = require('deep-freeze')
|
||||
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
|
Before Width: | Height: | Size: 636 KiB After Width: | Height: | Size: 636 KiB |
BIN
e2e/cypress/fixtures/images/cat.jpg
Normal file
|
After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 544 KiB After Width: | Height: | Size: 544 KiB |
BIN
e2e/cypress/fixtures/images/traffic.jpg
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
13
e2e/cypress/integration/dashboard-react.spec.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
describe('dashboard-react', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/dashboard-react')
|
||||
cy.get('.uppy-Dashboard-input').as('file-input')
|
||||
})
|
||||
|
||||
it('should render in React and show thumbnails', () => {
|
||||
cy.get('@file-input').attachFile(['images/cat.jpg', 'images/traffic.jpg'])
|
||||
cy.get('.uppy-Dashboard-Item-previewImg')
|
||||
.should('have.length', 2)
|
||||
.each((element) => expect(element).attr('src').to.include('blob:'))
|
||||
})
|
||||
})
|
||||
18
e2e/cypress/integration/dashboard-transloadit.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
describe('Dashboard with Transloadit', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/dashboard-transloadit')
|
||||
cy.get('.uppy-Dashboard-input').as('file-input')
|
||||
cy.intercept('/assemblies/*').as('assemblies')
|
||||
cy.intercept('/resumable/*').as('resumable')
|
||||
})
|
||||
|
||||
it('should upload cat image successfully', () => {
|
||||
cy.get('@file-input').attachFile('images/cat.jpg')
|
||||
cy.get('.uppy-StatusBar-actionBtn--upload').click()
|
||||
|
||||
cy.wait('@assemblies')
|
||||
cy.wait('@resumable')
|
||||
|
||||
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
|
||||
})
|
||||
})
|
||||
78
e2e/cypress/integration/dashboard-tus.spec.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
import type BaseTus from '@uppy/tus'
|
||||
|
||||
type Tus = BaseTus & {
|
||||
requests: { isPaused: boolean }
|
||||
}
|
||||
|
||||
describe('Dashboard with Tus', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/dashboard-tus')
|
||||
cy.get('.uppy-Dashboard-input').as('file-input')
|
||||
cy.intercept('/files/*').as('tus')
|
||||
cy.intercept('http://localhost:3020/url/*').as('url')
|
||||
cy.intercept('http://localhost:3020/search/unsplash/*').as('unsplash')
|
||||
})
|
||||
|
||||
it('should upload cat image successfully', () => {
|
||||
cy.get('@file-input').attachFile('images/cat.jpg')
|
||||
cy.get('.uppy-StatusBar-actionBtn--upload').click()
|
||||
|
||||
cy.wait('@tus')
|
||||
|
||||
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
|
||||
})
|
||||
|
||||
// TODO: figure out why we send a PATCH request while another PATCH is still pending,
|
||||
// resulting in a 423 upload is locked
|
||||
// logs: https://gist.github.com/Acconut/d17315d2718d2944aabe2941f268530d
|
||||
xit('should start exponential backoff when receiving HTTP 429', () => {
|
||||
cy.get('@file-input').attachFile(['images/cat.jpg', 'images/traffic.jpg'])
|
||||
cy.get('.uppy-StatusBar-actionBtn--upload').click()
|
||||
|
||||
cy.intercept(
|
||||
{ method: 'PATCH', pathname: '/files/*', times: 1 },
|
||||
{ statusCode: 429, body: {} },
|
||||
).as('patch')
|
||||
|
||||
cy.wait('@patch')
|
||||
|
||||
cy.window().then(({ uppy }) => {
|
||||
expect(uppy.getPlugin<Tus>('Tus').requests.isPaused).to.equal(true)
|
||||
cy.wait('@tus')
|
||||
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
|
||||
})
|
||||
})
|
||||
|
||||
it('should upload remote image with URL plugin', () => {
|
||||
cy.get('[data-cy="Url"]').click()
|
||||
cy.get('.uppy-Url-input').type('https://via.placeholder.com/600x400')
|
||||
cy.get('.uppy-Url-importButton').click()
|
||||
cy.get('.uppy-StatusBar-actionBtn--upload').click()
|
||||
cy.wait('@url')
|
||||
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
|
||||
})
|
||||
|
||||
it('should upload remote image with Unsplash plugin', () => {
|
||||
cy.get('[data-cy="Unsplash"]').click()
|
||||
cy.get('.uppy-SearchProvider-input').type('book')
|
||||
cy.get('.uppy-SearchProvider-searchButton').click()
|
||||
cy.wait('@unsplash')
|
||||
// Test that the author link is visible
|
||||
cy.get('.uppy-ProviderBrowserItem')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.root().click()
|
||||
// We have hover states that show the author
|
||||
// but we don't have hover in e2e, so we focus after the click
|
||||
// to get the same effect. Also tests keyboard users this way.
|
||||
cy.get('input[type="checkbox"]').focus()
|
||||
cy.get('a').should('have.css', 'display', 'block')
|
||||
})
|
||||
cy.get('.uppy-c-btn-primary').click()
|
||||
cy.get('.uppy-StatusBar-actionBtn--upload').click()
|
||||
cy.wait('@unsplash')
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(2000) // bad practice, but the request is successful before the status bar updates here
|
||||
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
|
||||
})
|
||||
})
|
||||
13
e2e/cypress/integration/dashboard-ui.spec.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
describe('dashboard-ui', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/dashboard-ui')
|
||||
cy.get('.uppy-Dashboard-input').as('file-input')
|
||||
})
|
||||
|
||||
it('should render thumbnails', () => {
|
||||
cy.get('@file-input').attachFile(['images/cat.jpg', 'images/traffic.jpg'])
|
||||
cy.get('.uppy-Dashboard-Item-previewImg')
|
||||
.should('have.length', 2)
|
||||
.each((element) => expect(element).attr('src').to.include('blob:'))
|
||||
})
|
||||
})
|
||||
14
e2e/cypress/integration/dashboard-vue.spec.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
describe('dashboard-vue', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/dashboard-vue')
|
||||
})
|
||||
|
||||
// Only Vue 3 works in Parcel if you use SFC's but Vue 3 is broken in Uppy:
|
||||
// https://github.com/transloadit/uppy/issues/2877
|
||||
xit('should render in Vue 3 and show thumbnails', () => {
|
||||
cy.get('@file-input').attachFile(['images/cat.jpg', 'images/traffic.jpg'])
|
||||
cy.get('.uppy-Dashboard-Item-previewImg')
|
||||
.should('have.length', 2)
|
||||
.each((element) => expect(element).attr('src').to.include('blob:'))
|
||||
})
|
||||
})
|
||||
22
e2e/cypress/plugins/index.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/// <reference types="cypress" />
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
||||
33
e2e/cypress/support/commands.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
//
|
||||
|
||||
/* eslint-disable-next-line import/no-extraneous-dependencies */
|
||||
import 'cypress-file-upload'
|
||||
|
||||
import { createFakeFile } from './createFakeFile'
|
||||
|
||||
Cypress.Commands.add('createFakeFile', createFakeFile)
|
||||
49
e2e/cypress/support/createFakeFile.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
createFakeFile: typeof createFakeFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface File {
|
||||
source: string
|
||||
name: string
|
||||
type: string
|
||||
data: Blob
|
||||
}
|
||||
|
||||
export function createFakeFile (name?: string, type?: string, b64?: string): File {
|
||||
if (!b64) b64 = 'PHN2ZyB2aWV3Qm94PSIwIDAgMTIwIDEyMCI+CiAgPGNpcmNsZSBjeD0iNjAiIGN5PSI2MCIgcj0iNTAiLz4KPC9zdmc+Cg=='
|
||||
if (!type) type = 'image/svg+xml'
|
||||
|
||||
// https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
|
||||
function base64toBlob (base64Data: string, contentType = '') {
|
||||
const sliceSize = 1024
|
||||
const byteCharacters = atob(base64Data)
|
||||
const bytesLength = byteCharacters.length
|
||||
const slicesCount = Math.ceil(bytesLength / sliceSize)
|
||||
const byteArrays = new Array(slicesCount)
|
||||
|
||||
for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
|
||||
const begin = sliceIndex * sliceSize
|
||||
const end = Math.min(begin + sliceSize, bytesLength)
|
||||
|
||||
const bytes = new Array(end - begin)
|
||||
for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
|
||||
bytes[i] = byteCharacters[offset].charCodeAt(0)
|
||||
}
|
||||
byteArrays[sliceIndex] = new Uint8Array(bytes)
|
||||
}
|
||||
return new Blob(byteArrays, { type: contentType })
|
||||
}
|
||||
|
||||
const blob = base64toBlob(b64, type)
|
||||
|
||||
return {
|
||||
source: 'test',
|
||||
name: name || 'test-file',
|
||||
type: blob.type,
|
||||
data: blob,
|
||||
}
|
||||
}
|
||||
24
e2e/cypress/support/index.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
import './commands.ts'
|
||||
|
||||
import type { Uppy } from '@uppy/core'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
uppy: Uppy
|
||||
}
|
||||
}
|
||||
85
e2e/generate-test.mjs
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env node
|
||||
/* eslint-disable no-console, import/no-extraneous-dependencies */
|
||||
import prompts from 'prompts'
|
||||
import fs from 'node:fs/promises'
|
||||
import dedent from 'dedent'
|
||||
|
||||
const packageNames = await fs.readdir(new URL('../packages/@uppy', import.meta.url))
|
||||
const unwantedPackages = ['core', 'companion', 'redux-dev-tools', 'utils']
|
||||
|
||||
const { name } = await prompts({
|
||||
type: 'text',
|
||||
name: 'name',
|
||||
message: 'What should the name of the test be (e.g `dashboard-tus`)?',
|
||||
validate: (value) => /^[a-z|-]+$/i.test(value),
|
||||
})
|
||||
|
||||
const { packages } = await prompts({
|
||||
type: 'multiselect',
|
||||
name: 'packages',
|
||||
message: 'What packages do you want to test?',
|
||||
hint: '@uppy/core is automatically included',
|
||||
choices: packageNames
|
||||
.filter((pkg) => !unwantedPackages.includes(pkg))
|
||||
.map((pkg) => ({ title: pkg, value: pkg })),
|
||||
})
|
||||
|
||||
const camelcase = (str) => str
|
||||
.toLowerCase()
|
||||
.replace(/([-][a-z])/g, (group) => group.toUpperCase().replace('-', ''))
|
||||
|
||||
const testUrl = new URL(`cypress/integration/${name}.spec.ts`, import.meta.url)
|
||||
const test = dedent`
|
||||
describe('${name}', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/${name}')
|
||||
})
|
||||
})
|
||||
`
|
||||
const htmlUrl = new URL(`clients/${name}/index.html`, import.meta.url)
|
||||
const html = dedent`
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>${name}</title>
|
||||
<script defer type="module" src="app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
const appUrl = new URL(`clients/${name}/app.js`, import.meta.url)
|
||||
// dedent is acting weird for this one but this formatting fixes it.
|
||||
const app = dedent`
|
||||
import Uppy from '@uppy/core'
|
||||
${packages.map((pgk) => `import ${camelcase(pgk)} from '@uppy/${pgk}'`).join('\n')}
|
||||
|
||||
const uppy = new Uppy()
|
||||
${packages.map((pkg) => `.use(${camelcase(pkg)})`).join('\n\t')}
|
||||
|
||||
// Keep this here to access uppy in tests
|
||||
window.uppy = uppy
|
||||
`
|
||||
|
||||
await fs.writeFile(testUrl, test)
|
||||
await fs.mkdir(new URL(`clients/${name}`, import.meta.url))
|
||||
await fs.writeFile(htmlUrl, html)
|
||||
await fs.writeFile(appUrl, app)
|
||||
|
||||
const homeUrl = new URL('clients/index.html', import.meta.url)
|
||||
const home = await fs.readFile(homeUrl, 'utf8')
|
||||
const newHome = home.replace(
|
||||
'</ul>',
|
||||
`\t<li><a href="${name}/index.html">${name}</a></li>\n\t\t\t</ul>`,
|
||||
)
|
||||
await fs.writeFile(homeUrl, newHome)
|
||||
|
||||
const prettyPath = (url) => url.toString().split('uppy', 2)[1]
|
||||
|
||||
console.log(`✅ Generated ${prettyPath(testUrl)}`)
|
||||
console.log(`✅ Generated ${prettyPath(htmlUrl)}`)
|
||||
console.log(`✅ Generated ${prettyPath(appUrl)}`)
|
||||
console.log(`✅ Updated ${prettyPath(homeUrl)}`)
|
||||
59
e2e/package.json
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"name": "e2e",
|
||||
"private": true,
|
||||
"author": "Merlijn Vos <merlijn@soverin.net>",
|
||||
"description": "End-to-end test suite for Uppy",
|
||||
"scripts": {
|
||||
"client:start": "parcel clients/index.html",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:headless": "cypress run",
|
||||
"generate-test": "yarn node generate-test.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/audio": "workspace:^",
|
||||
"@uppy/aws-s3": "workspace:^",
|
||||
"@uppy/aws-s3-multipart": "workspace:^",
|
||||
"@uppy/box": "workspace:^",
|
||||
"@uppy/companion-client": "workspace:^",
|
||||
"@uppy/core": "workspace:^",
|
||||
"@uppy/dashboard": "workspace:^",
|
||||
"@uppy/drag-drop": "workspace:^",
|
||||
"@uppy/drop-target": "workspace:^",
|
||||
"@uppy/dropbox": "workspace:^",
|
||||
"@uppy/facebook": "workspace:^",
|
||||
"@uppy/file-input": "workspace:^",
|
||||
"@uppy/form": "workspace:^",
|
||||
"@uppy/golden-retriever": "workspace:^",
|
||||
"@uppy/google-drive": "workspace:^",
|
||||
"@uppy/image-editor": "workspace:^",
|
||||
"@uppy/informer": "workspace:^",
|
||||
"@uppy/instagram": "workspace:^",
|
||||
"@uppy/onedrive": "workspace:^",
|
||||
"@uppy/progress-bar": "workspace:^",
|
||||
"@uppy/provider-views": "workspace:^",
|
||||
"@uppy/screen-capture": "workspace:^",
|
||||
"@uppy/status-bar": "workspace:^",
|
||||
"@uppy/store-default": "workspace:^",
|
||||
"@uppy/store-redux": "workspace:^",
|
||||
"@uppy/thumbnail-generator": "workspace:^",
|
||||
"@uppy/transloadit": "workspace:^",
|
||||
"@uppy/tus": "workspace:^",
|
||||
"@uppy/unsplash": "workspace:^",
|
||||
"@uppy/url": "workspace:^",
|
||||
"@uppy/webcam": "workspace:^",
|
||||
"@uppy/xhr-upload": "workspace:^",
|
||||
"@uppy/zoom": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^9.0.0",
|
||||
"cypress-file-upload": "^5.0.8",
|
||||
"dedent": "^0.7.0",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"parcel": "^2.0.1",
|
||||
"prompts": "^2.4.2",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"typescript": "^4.5.4",
|
||||
"vue": "next"
|
||||
}
|
||||
}
|
||||
8
e2e/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress", "cypress-file-upload"]
|
||||
},
|
||||
"include": ["cypress/**/*.ts"]
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
# Rename this file to env.sh, it will be kept out of Git.
|
||||
# So suitable for adding secret keys and such
|
||||
|
||||
export NODE_ENV="${NODE_ENV:-development}"
|
||||
export COMPANION_DROPBOX_KEY="***"
|
||||
export COMPANION_DROPBOX_SECRET="***"
|
||||
export COMPANION_BOX_KEY="***"
|
||||
export COMPANION_BOX_SECRET="***"
|
||||
export COMPANION_GOOGLE_KEY="***"
|
||||
export COMPANION_GOOGLE_SECRET="***"
|
||||
export COMPANION_INSTAGRAM_KEY="***"
|
||||
export COMPANION_INSTAGRAM_SECRET="***"
|
||||
export COMPANION_FACEBOOK_KEY="***"
|
||||
export COMPANION_FACEBOOK_SECRET="***"
|
||||
export EDGLY_KEY="***"
|
||||
export EDGLY_SECRET="***"
|
||||
export GITHUB_TOKEN="***"
|
||||
export COMPANION_ZOOM_KEY="***"
|
||||
export COMPANION_ZOOM_SECRET="***"
|
||||
|
||||
# Let's not set this by default, because that will make acceptance tests Always run on Saucelabs
|
||||
## export SAUCE_ACCESS_KEY="***"
|
||||
## export SAUCE_USERNAME="***"
|
||||
|
||||
# travis encrypt --add GHPAGES_URL=https://secret_access_token@github.com/transloadit/uppy.git
|
||||
# travis encrypt --add env.global "COMPANION_DROPBOX_KEY=${COMPANION_DROPBOX_KEY}"
|
||||
# travis encrypt --add env.global "COMPANION_DROPBOX_SECRET=${COMPANION_DROPBOX_SECRET}"
|
||||
# travis encrypt --add env.global "COMPANION_BOX_KEY=${COMPANION_BOX_KEY}"
|
||||
# travis encrypt --add env.global "COMPANION_BOX_SECRET=${COMPANION_BOX_SECRET}"
|
||||
# travis encrypt --add env.global "COMPANION_GOOGLE_KEY=${COMPANION_GOOGLE_KEY}"
|
||||
# travis encrypt --add env.global "COMPANION_GOOGLE_SECRET=${COMPANION_GOOGLE_SECRET}"
|
||||
# travis encrypt --add env.global "COMPANION_INSTAGRAM_KEY=${COMPANION_INSTAGRAM_KEY}"
|
||||
# travis encrypt --add env.global "COMPANION_INSTAGRAM_SECRET=${COMPANION_INSTAGRAM_SECRET}"
|
||||
|
||||
# The Travis Sauce Connect addon exports the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables,
|
||||
# and relays connections to the hub URL back to Sauce Labs.
|
||||
# See: https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-Sauce-Labs
|
||||
# travis encrypt --add addons.sauce_connect.username "${SAUCE_USERNAME}"
|
||||
# travis encrypt --add addons.sauce_connect.access_key "${SAUCE_ACCESS_KEY}"
|
||||
15
package.json
|
|
@ -34,7 +34,8 @@
|
|||
"packages/uppy",
|
||||
"private/*",
|
||||
"test/endtoend",
|
||||
"website"
|
||||
"website",
|
||||
"e2e"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.14.5",
|
||||
|
|
@ -47,6 +48,7 @@
|
|||
"@babel/preset-env": "^7.14.7",
|
||||
"@babel/register": "^7.10.5",
|
||||
"@goto-bus-stop/envify": "^5.0.0",
|
||||
"@parcel/transformer-vue": "^2.2.1",
|
||||
"@size-limit/preset-big-lib": "7.0.5",
|
||||
"@size-limit/webpack-why": "^7.0.5",
|
||||
"@types/jasmine": "file:./private/@types/jasmine",
|
||||
|
|
@ -64,12 +66,12 @@
|
|||
"core-js": "~3.19.3",
|
||||
"cssnano": "^5.0.6",
|
||||
"dedent": "^0.7.0",
|
||||
"deep-freeze": "^0.0.1",
|
||||
"esbuild": "^0.14.1",
|
||||
"esbuild-plugin-babel": "^0.2.3",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-transloadit": "^2.0.0",
|
||||
"eslint-plugin-compat": "^4.0.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^25.0.0",
|
||||
"eslint-plugin-jsdoc": "^37.0.0",
|
||||
|
|
@ -106,6 +108,7 @@
|
|||
"resolve": "^1.17.0",
|
||||
"sass": "^1.29.0",
|
||||
"size-limit": "7.0.5",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"stylelint": "^13.13.1",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
|
|
@ -139,8 +142,14 @@
|
|||
"lint": "eslint . --cache",
|
||||
"release": "PACKAGES=$(yarn workspaces list --json) yarn workspace @uppy-dev/release interactive",
|
||||
"size": "echo 'JS Bundle mingz:' && cat ./packages/uppy/dist/uppy.min.js | gzip | wc -c && echo 'CSS Bundle mingz:' && cat ./packages/uppy/dist/uppy.min.css | gzip | wc -c",
|
||||
"start:companion": "bash ./bin/companion",
|
||||
"start:companion": "bash bin/companion.sh",
|
||||
"start": "npm-run-all --parallel watch start:companion web:start",
|
||||
"e2e": "npm-run-all --parallel watch:js:lib e2e:client start:companion e2e:cypress",
|
||||
"e2e:ci": "start-server-and-test 'npm-run-all --parallel e2e:client start:companion' '1234|3020' e2e:headless",
|
||||
"e2e:client": "yarn workspace e2e client:start",
|
||||
"e2e:cypress": "yarn workspace e2e cypress:open",
|
||||
"e2e:headless": "yarn workspace e2e cypress:headless",
|
||||
"e2e:generate": "yarn workspace e2e generate-test",
|
||||
"test:companion": "yarn workspace @uppy/companion test",
|
||||
"test:endtoend:local": "yarn workspace @uppy-tests/end2end test:endtoend:local",
|
||||
"test:endtoend": "yarn workspace @uppy-tests/end2end test:endtoend",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable max-classes-per-file */
|
||||
/* global AggregateError */
|
||||
|
||||
'use strict'
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ const path = require('path')
|
|||
const prettierBytes = require('@transloadit/prettier-bytes')
|
||||
const Core = require('./index')
|
||||
const UIPlugin = require('./UIPlugin')
|
||||
const AcquirerPlugin1 = require('../../../../test/mocks/acquirerPlugin1')
|
||||
const AcquirerPlugin2 = require('../../../../test/mocks/acquirerPlugin2')
|
||||
const InvalidPlugin = require('../../../../test/mocks/invalidPlugin')
|
||||
const InvalidPluginWithoutId = require('../../../../test/mocks/invalidPluginWithoutId')
|
||||
const InvalidPluginWithoutType = require('../../../../test/mocks/invalidPluginWithoutType')
|
||||
const DeepFrozenStore = require('../../../../test/resources/DeepFrozenStore.js')
|
||||
const AcquirerPlugin1 = require('./mocks/acquirerPlugin1')
|
||||
const AcquirerPlugin2 = require('./mocks/acquirerPlugin2')
|
||||
const InvalidPlugin = require('./mocks/invalidPlugin')
|
||||
const InvalidPluginWithoutId = require('./mocks/invalidPluginWithoutId')
|
||||
const InvalidPluginWithoutType = require('./mocks/invalidPluginWithoutType')
|
||||
const DeepFrozenStore = require('../../../../e2e/cypress/fixtures/DeepFrozenStore.js')
|
||||
|
||||
jest.mock('nanoid/non-secure', () => {
|
||||
return { nanoid: () => 'cjd09qwxb000dlql4tp4doz8h' }
|
||||
|
|
@ -18,7 +18,7 @@ jest.mock('@uppy/utils/lib/findDOMElement', () => {
|
|||
return () => null
|
||||
})
|
||||
|
||||
const sampleImage = fs.readFileSync(path.join(__dirname, '../../../../test/resources/image.jpg'))
|
||||
const sampleImage = fs.readFileSync(path.join(__dirname, '../../../../e2e/cypress/fixtures/images/image.jpg'))
|
||||
|
||||
describe('src/Core', () => {
|
||||
const RealCreateObjectUrl = global.URL.createObjectURL
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const { UIPlugin } = require('../../packages/@uppy/core')
|
||||
const UIPlugin = require('../UIPlugin')
|
||||
|
||||
module.exports = class TestSelector1 extends UIPlugin {
|
||||
constructor (uppy, opts) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const { UIPlugin } = require('../../packages/@uppy/core')
|
||||
const UIPlugin = require('../UIPlugin')
|
||||
|
||||
module.exports = class TestSelector2 extends UIPlugin {
|
||||
constructor (uppy, opts) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const { UIPlugin } = require('../../packages/@uppy/core')
|
||||
const UIPlugin = require('../UIPlugin')
|
||||
|
||||
module.exports = class InvalidPluginWithoutName extends UIPlugin {
|
||||
constructor (uppy, opts) {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
const { UIPlugin } = require('../../packages/@uppy/core')
|
||||
const UIPlugin = require('../UIPlugin')
|
||||
|
||||
module.exports = class InvalidPluginWithoutType extends UIPlugin {
|
||||
constructor (uppy, opts) {
|
||||
|
|
@ -125,6 +125,7 @@ class AddFiles extends Component {
|
|||
className="uppy-u-reset uppy-c-btn uppy-DashboardTab-btn"
|
||||
role="tab"
|
||||
tabIndex={0}
|
||||
data-cy={acquirer.id}
|
||||
aria-controls={`uppy-DashboardContent-panel--${acquirer.id}`}
|
||||
aria-selected={this.props.activePickerPanel.id === acquirer.id}
|
||||
data-uppy-super-focusable
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
VITE_UPLOADER=tus
|
||||
|
||||
#### To customize this, create a .env.local file that overrides those values
|
||||
|
||||
# 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
|
||||
|
||||
### DEV CONFIG: Endpoint URLs
|
||||
|
||||
VITE_COMPANION_URL=http://localhost:3020
|
||||
VITE_TUS_ENDPOINT=https://tusd.tusdemo.net/files/
|
||||
VITE_XHR_ENDPOINT=https://xhr-server.herokuapp.com/upload
|
||||
|
||||
### DEV CONFIG: Transloadit keys
|
||||
VITE_TRANSLOADIT_KEY=...
|
||||
VITE_TRANSLOADIT_TEMPLATE=...
|
||||
VITE_TRANSLOADIT_SERVICE_URL=https://api2.transloadit.com
|
||||
|
|
@ -6,6 +6,9 @@
|
|||
"@uppy": "../../packages/@uppy"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@uppy/companion": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.4.4",
|
||||
"@babel/plugin-transform-react-jsx": "^7.10.4",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ let counter = 0
|
|||
* @type {import('vite').UserConfig}
|
||||
*/
|
||||
const config = {
|
||||
envDir: fileURLToPath(ROOT),
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
defaultIsModuleExports: true,
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
#main { display: flex; } /* display side by side */
|
||||
</style>
|
||||
<main>
|
||||
<h2>Chaos monkey</h2>
|
||||
<div id="main">
|
||||
<div id="dash"></div>
|
||||
<div>
|
||||
<ol id="log"></ol>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script>
|
||||
function addLogMessage (message) {
|
||||
var log = document.querySelector('#log')
|
||||
while (log.childNodes.length >= 15) {
|
||||
log.removeChild(log.firstChild)
|
||||
log.start = (parseInt(log.start, 10) || 0) + 1
|
||||
}
|
||||
var li = document.createElement('li')
|
||||
li.appendChild(document.createTextNode(message))
|
||||
log.appendChild(li)
|
||||
}
|
||||
</script>
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Tus from '@uppy/tus'
|
||||
import canvasToBlob from '@uppy/utils/lib/canvasToBlob'
|
||||
|
||||
const endpoint = 'http://localhost:1081'
|
||||
|
||||
let id = 0
|
||||
window.setup = function setup (options) {
|
||||
id += 1
|
||||
|
||||
// Initialise Uppy with Drag & Drop
|
||||
const uppy = new Uppy({ id: `uppy${id}`, debug: true })
|
||||
|
||||
uppy.use(Dashboard, { inline: true, target: '#dash' })
|
||||
uppy.use(Tus, {
|
||||
endpoint: `${endpoint}/files/`,
|
||||
limit: options.limit,
|
||||
})
|
||||
uppy.on('file-added', (file) => {
|
||||
randomColorImage().then((blob) => {
|
||||
uppy.setFileState(file.id, {
|
||||
// eslint-disable-next-line compat/compat
|
||||
preview: URL.createObjectURL(blob),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return uppy
|
||||
}
|
||||
|
||||
function randomColorImage () {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 140
|
||||
canvas.height = 140
|
||||
const context = canvas.getContext('2d')
|
||||
context.fillStyle = '#xxxxxx'.replace(/x/g, () => '0123456789ABCDEF'[Math.floor(Math.random() * 16)])
|
||||
context.fillRect(0, 0, 140, 140)
|
||||
return canvasToBlob(canvas)
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/* global browser */
|
||||
const crypto = require('crypto')
|
||||
const { selectFakeFile } = require('../utils')
|
||||
|
||||
const testURL = 'http://localhost:4567/chaos-monkey'
|
||||
|
||||
describe('Chaos monkey', function test () {
|
||||
this.timeout(5 * 60 * 1000) // 5 minutes
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('Add and cancel a bunch', async () => {
|
||||
await browser.execute(() => {
|
||||
window.currentUppy = window.setup({ limit: 3 })
|
||||
window.onerror = (message) => {
|
||||
window.anyError = message
|
||||
}
|
||||
})
|
||||
|
||||
const types = ['application/octet-stream', 'text/plain']
|
||||
const generate = {
|
||||
'application/octet-stream' () {
|
||||
const len = Math.round(Math.random() * 5_000_000)
|
||||
return crypto.randomBytes(len)
|
||||
},
|
||||
'text/plain' () {
|
||||
const len = Math.round(Math.random() * 5_000_000 / 'Lorem ipsum'.length)
|
||||
return Buffer.from('Lorem ipsum'.repeat(len))
|
||||
},
|
||||
}
|
||||
|
||||
async function addFile () {
|
||||
await browser.execute(() => {
|
||||
window.addLogMessage('Adding a file')
|
||||
})
|
||||
const type = types[Math.floor(Math.random() * types.length)]
|
||||
const data = generate[type]().toString('base64')
|
||||
|
||||
const name = `${Math.random().toString(32).slice(2)}-file`
|
||||
await browser.execute(selectFakeFile, 'currentUppy', name, type, data)
|
||||
}
|
||||
|
||||
function cancelFile () {
|
||||
return browser.execute(() => {
|
||||
window.addLogMessage('Cancelling a file')
|
||||
// prefer deleting a file that is uploading right now
|
||||
const selector = Math.random() <= 0.7
|
||||
? '.is-inprogress .uppy-Dashboard-Item-action--remove'
|
||||
: '.uppy-Dashboard-Item-action--remove'
|
||||
const buttons = document.querySelectorAll(selector)
|
||||
const del = buttons[Math.floor(Math.random() * buttons.length)]
|
||||
if (del) del.click()
|
||||
})
|
||||
}
|
||||
|
||||
function startUploadIfAnyWaitingFiles () {
|
||||
return browser.execute(() => {
|
||||
window.addLogMessage('Starting upload')
|
||||
const start = document.querySelector('.uppy-StatusBar-actionBtn--upload')
|
||||
if (start) start.click()
|
||||
})
|
||||
}
|
||||
|
||||
function cancelAll () {
|
||||
return browser.execute(() => {
|
||||
window.addLogMessage('Cancelling everything')
|
||||
const button = document.querySelector('.uppy-DashboardContent-back')
|
||||
if (button) button.click()
|
||||
})
|
||||
}
|
||||
|
||||
await addFile()
|
||||
await addFile()
|
||||
await addFile()
|
||||
|
||||
for (let i = 0; i < 300; i++) {
|
||||
await browser.pause(50 + Math.floor(Math.random() * 300))
|
||||
const v = Math.floor(Math.random() * 100)
|
||||
if (v < 45) {
|
||||
await addFile()
|
||||
} else if (v < 55) {
|
||||
await cancelFile()
|
||||
} else if (v === 55) {
|
||||
await cancelAll()
|
||||
} else if (v < 75) {
|
||||
await startUploadIfAnyWaitingFiles()
|
||||
} else {
|
||||
// wait
|
||||
}
|
||||
}
|
||||
|
||||
await cancelAll()
|
||||
|
||||
const errorMessage = await browser.execute(() => {
|
||||
return window.anyError
|
||||
})
|
||||
// yikes chai, why can this not be a function call
|
||||
expect(errorMessage).to.not.exist // eslint-disable-line no-unused-expressions
|
||||
})
|
||||
})
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
SKIP_PREFLIGHT_CHECK=true
|
||||
DISABLE_ESLINT_PLUGIN=true
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "React"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
test/endtoend/create-react-app/.gitignore
vendored
|
|
@ -1,21 +0,0 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
36601
test/endtoend/create-react-app/package-lock.json
generated
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"name": "create-react-app",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "/create-react-app/",
|
||||
"dependencies": {
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.8 KiB |
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #222;
|
||||
height: 150px;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-title {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.App-intro {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
// These are resolved from the root instead of from the local package.json in
|
||||
// the create-react-app e2e test code.
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import Uppy from '@uppy/core'
|
||||
import Tus from '@uppy/tus'
|
||||
import GoogleDrive from '@uppy/google-drive'
|
||||
import { Dashboard, DashboardModal } from '@uppy/react'
|
||||
import '@uppy/core/dist/style.css'
|
||||
import '@uppy/dashboard/dist/style.css'
|
||||
/* eslint-enable import/no-extraneous-dependencies */
|
||||
|
||||
const endpoint = 'http://localhost:1080'
|
||||
|
||||
class App extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.uppy = new Uppy({ id: 'uppy1', autoProceed: true, debug: true })
|
||||
.use(Tus, { endpoint: `${endpoint}/files/` })
|
||||
.use(GoogleDrive, { companionUrl: 'https://companion.uppy.io' })
|
||||
|
||||
this.uppy2 = new Uppy({ id: 'uppy2', autoProceed: false, debug: true })
|
||||
.use(Tus, { endpoint: `${endpoint}/files/` })
|
||||
|
||||
this.state = {
|
||||
showInlineDashboard: true,
|
||||
open: false,
|
||||
}
|
||||
|
||||
this.handleModalClick = this.handleModalClick.bind(this)
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.uppy.close()
|
||||
this.uppy2.close()
|
||||
}
|
||||
|
||||
handleModalClick () {
|
||||
this.setState(({ open }) => ({
|
||||
open: !open,
|
||||
}))
|
||||
}
|
||||
|
||||
render () {
|
||||
const { showInlineDashboard, open } = this.state
|
||||
return (
|
||||
<div>
|
||||
<h1>React Examples</h1>
|
||||
|
||||
<h2>Inline Dashboard</h2>
|
||||
<div id="inline-dashboard">
|
||||
<label htmlFor="inline-dashboard-toggle">
|
||||
<input
|
||||
id="inline-dashboard-toggle"
|
||||
type="checkbox"
|
||||
checked={showInlineDashboard}
|
||||
onChange={(event) => {
|
||||
this.setState({
|
||||
showInlineDashboard: event.target.checked,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
Show Dashboard
|
||||
</label>
|
||||
{showInlineDashboard && (
|
||||
<Dashboard
|
||||
uppy={this.uppy}
|
||||
plugins={['GoogleDrive']}
|
||||
metaFields={[
|
||||
{ id: 'name', name: 'Name', placeholder: 'File name' },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<h2>Modal Dashboard</h2>
|
||||
<div id="modal-dashboard">
|
||||
<button onClick={this.handleModalClick} id="modal-dashboard-toggle" type="button">
|
||||
{open ? 'Close dashboard' : 'Open dashboard'}
|
||||
</button>
|
||||
<DashboardModal
|
||||
uppy={this.uppy2}
|
||||
open={open}
|
||||
target="#modal-dashboard"
|
||||
onRequestClose={() => this.setState({ open: false })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
|
||||
const errors = []
|
||||
window.onerror = (err) => {
|
||||
errors.push(err)
|
||||
}
|
||||
window.errors = errors
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
|
||||
<g fill="#61DAFB">
|
||||
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
|
||||
<circle cx="420.9" cy="296.5" r="45.7"/>
|
||||
<path d="M520.5 78.1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
|
@ -1,99 +0,0 @@
|
|||
/* global capabilities, browser, $, $$ */
|
||||
const testURL = 'http://localhost:4567/create-react-app'
|
||||
|
||||
describe('webpack build', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should include CSS', async () => {
|
||||
const el = await $('#inline-dashboard .uppy-Dashboard-inner')
|
||||
await el.waitForExist()
|
||||
const bgColor = await el.getCSSProperty('background-color')
|
||||
// computed value is rgb() or rgba(), not hex (but listing it here to show the expected value too)
|
||||
expect(/^rgba?\(250, ?250, ?250(?:, ?1)?\)$|^#fafafa$/.test(bgColor.value)).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('React: Dashboard', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should have Google Drive panel', async () => {
|
||||
const el = await $('#inline-dashboard .uppy-Dashboard-inner')
|
||||
await el.waitForExist()
|
||||
|
||||
const tabs = await $$('.uppy-DashboardTab-name')
|
||||
let hasGDrive = false
|
||||
for (const name of tabs) {
|
||||
hasGDrive = (await name.getText()) === 'Google Drive'
|
||||
if (hasGDrive) break
|
||||
}
|
||||
expect(hasGDrive).to.equal(true)
|
||||
})
|
||||
|
||||
it('should survive being mounted and unmounted', async () => {
|
||||
const el = await $('#inline-dashboard .uppy-Dashboard-inner')
|
||||
await el.waitForExist()
|
||||
|
||||
async function toggle () {
|
||||
const button = await $('#inline-dashboard-toggle')
|
||||
await button.click()
|
||||
await browser.pause(250)
|
||||
}
|
||||
|
||||
// close
|
||||
await toggle()
|
||||
// open
|
||||
await toggle()
|
||||
// close
|
||||
await toggle()
|
||||
// open
|
||||
await toggle()
|
||||
|
||||
// open GDrive panel
|
||||
const gdriveButton = await $('.uppy-DashboardTab[data-uppy-acquirer-id="GoogleDrive"] button')
|
||||
await gdriveButton.click()
|
||||
await browser.pause(500)
|
||||
|
||||
// side effecting property access, not a function!
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
expect(await $('.uppy-Provider-authBtn')).to.exist
|
||||
})
|
||||
})
|
||||
|
||||
describe('React: DashboardModal', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should have controlled open and close', async function test () {
|
||||
// Does not work on Safari 13.1 right now
|
||||
if (capabilities.browserName === 'safari' && capabilities.browserVersion === '13.1') {
|
||||
this.skip()
|
||||
return
|
||||
}
|
||||
|
||||
const modalToggle = await $('#modal-dashboard-toggle')
|
||||
const modalWrapper = await $('#modal-dashboard .uppy-Dashboard--modal')
|
||||
const modalClose = await $('#modal-dashboard .uppy-Dashboard-close')
|
||||
|
||||
await modalToggle.waitForExist()
|
||||
|
||||
expect(await modalWrapper.getAttribute('aria-hidden')).to.equal('true')
|
||||
|
||||
await modalToggle.click()
|
||||
await browser.pause(50) // wait for the animation to start
|
||||
|
||||
// Edge appears to report empty string while others report null
|
||||
expect(await modalWrapper.getAttribute('aria-hidden')).to.be.oneOf([null, 'false'])
|
||||
|
||||
await browser.pause(500) // wait for the animation to complete
|
||||
|
||||
await modalClose.click()
|
||||
await browser.pause(500) // wait for the animation to complete
|
||||
|
||||
expect(await modalWrapper.getAttribute('aria-hidden')).to.equal('true')
|
||||
})
|
||||
})
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#uppyi18n-progress {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy DragDrop i18n + XHRUpload</h2>
|
||||
<div>
|
||||
<div id="uppyi18n"></div>
|
||||
<div id="uppyi18n-progress"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import DragDrop from '@uppy/drag-drop'
|
||||
import XHRUpload from '@uppy/xhr-upload'
|
||||
import ProgressBar from '@uppy/progress-bar'
|
||||
|
||||
import DeepFrozenStore from '../../resources/DeepFrozenStore.js'
|
||||
|
||||
const uppyi18n = new Uppy({
|
||||
id: 'uppyi18n',
|
||||
debug: true,
|
||||
autoProceed: true,
|
||||
store: DeepFrozenStore(),
|
||||
})
|
||||
|
||||
uppyi18n
|
||||
.use(DragDrop, {
|
||||
target: '#uppyi18n',
|
||||
locale: {
|
||||
strings: {
|
||||
dropHereOr: 'Перенесите файлы сюда или %{browse}',
|
||||
browse: 'выберите',
|
||||
},
|
||||
},
|
||||
})
|
||||
.use(ProgressBar, { target: '#uppyi18n-progress' })
|
||||
.use(XHRUpload, { endpoint: 'https://xhr-server.herokuapp.com/upload' })
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/* global browser */
|
||||
// const path = require('path')
|
||||
const { selectFakeFile, ensureInputVisible } = require('../utils')
|
||||
|
||||
const testURL = 'http://localhost:4567/i18n-drag-drop'
|
||||
|
||||
describe('File upload with DragDrop + XHRUpload, i18n translated string', function test () {
|
||||
this.retries(2)
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
await browser.execute(ensureInputVisible, '#uppyi18n .uppy-DragDrop-input')
|
||||
})
|
||||
|
||||
it('should upload a file with XHRUpload and set progressbar to 100%', async () => {
|
||||
// const testImage = path.join(__dirname, '../../resources/image.jpg')
|
||||
// if (supportsChooseFile(capabilities)) {
|
||||
// const input = await browser.$('#uppyi18n .uppy-DragDrop-input')
|
||||
// await input.setValue(testImage)
|
||||
// } else {
|
||||
// await browser.execute(selectFakeFile, 'uppyi18n')
|
||||
// }
|
||||
await browser.execute(selectFakeFile, 'uppyi18n')
|
||||
await browser.pause(5000)
|
||||
const percent = await browser.$('#uppyi18n-progress .uppy-ProgressBar-percentage')
|
||||
const html = await percent.getHTML(false)
|
||||
expect(Number(html)).to.be.equal(100)
|
||||
})
|
||||
|
||||
it('should translate text strings into Russian', async () => {
|
||||
const label = await browser.$('#uppyi18n .uppy-DragDrop-label')
|
||||
const text = await label.getText()
|
||||
expect(text.trim()).to.be.equal('Перенесите файлы сюда или выберите')
|
||||
})
|
||||
})
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"name": "@uppy-tests/end2end",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@wdio/cli": "^7.7.7",
|
||||
"@wdio/local-runner": "^7.7.7",
|
||||
"@wdio/mocha-framework": "^7.7.7",
|
||||
"@wdio/sauce-service": "^7.7.7",
|
||||
"brake": "^1.0.1",
|
||||
"chai": "^4.2.0",
|
||||
"esbuild": "^0.14.1",
|
||||
"express": "^4.17.1",
|
||||
"glob": "^7.2.0",
|
||||
"http-proxy": "^1.18.1",
|
||||
"minimist": "^1.2.5",
|
||||
"tus-node-server": "^0.3.2",
|
||||
"webdriverio": "^7.7.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test:endtoend:build-ci": "cd ../.. && bash ./bin/endtoend-build-ci",
|
||||
"test:endtoend:build": "cd ../.. && bash ./bin/endtoend-build",
|
||||
"test:endtoend:local": "yarn run test:endtoend:build && wdio wdio.local.conf.js",
|
||||
"test:endtoend:providers": "yarn run test:endtoend:build && . ./providers/env.sh && wdio wdio.local.conf.js --spec providers/provider.*.test.js",
|
||||
"test:endtoend:prepare-ci": "npm-run-all --parallel --race test:endtoend:registry test:endtoend:build-ci",
|
||||
"test:endtoend:registry": "verdaccio --listen 4002 --config verdaccio.yaml",
|
||||
"test:endtoend": "yarn run test:endtoend:prepare-ci && wdio wdio.remote.conf.js"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export UPPY_INSTAGRAM_USERNAME="***"
|
||||
export UPPY_INSTAGRAM_PASSWORD="***"
|
||||
export UPPY_GOOGLE_EMAIL="***"
|
||||
export UPPY_GOOGLE_PASSWORD="****"
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
exports.finishUploadTest = async (browser) => {
|
||||
// switch back to uppy tab
|
||||
await browser.switchWindow(/localhost/)
|
||||
const fileItem = await browser.$('.uppy-ProviderBrowser-list li.uppy-ProviderBrowserItem:last-child button')
|
||||
await fileItem.waitForDisplayed()
|
||||
await fileItem.click()
|
||||
|
||||
const uploadButton = await browser.$('.uppy-ProviderBrowser-footer .uppy-u-reset.uppy-c-btn.uppy-c-btn-primary')
|
||||
await uploadButton.click()
|
||||
const completeBar = await browser.$('.uppy-StatusBar-content[title="Complete"]')
|
||||
await completeBar.waitForDisplayed(20000)
|
||||
}
|
||||
|
||||
exports.startUploadTest = async (browser, providerName, tabMatch) => {
|
||||
const providerButton = await browser.$(
|
||||
`.uppy-DashboardTab-btn[aria-controls=uppy-DashboardContent-panel--${providerName}]`,
|
||||
)
|
||||
await providerButton.click()
|
||||
await browser.pause(2000)
|
||||
const authButton = await browser.$('.uppy-Provider-authBtn')
|
||||
await authButton.waitForDisplayed()
|
||||
await authButton.click()
|
||||
await browser.pause(5000)
|
||||
// move control to provider oauth tab
|
||||
await browser.switchWindow(tabMatch)
|
||||
}
|
||||
|
||||
exports.uploadWithRetry = async (browser, providerName, testURL) => {
|
||||
await browser.url(`${testURL}?socketerr=true`)
|
||||
|
||||
const providerButton = await browser.$(
|
||||
`.uppy-DashboardTab-btn[aria-controls=uppy-DashboardContent-panel--${providerName}]`,
|
||||
)
|
||||
await providerButton.click()
|
||||
await browser.pause(2000)
|
||||
|
||||
const fileItem = await browser.$('.uppy-ProviderBrowser-list li.uppy-ProviderBrowserItem:last-child button')
|
||||
await fileItem.waitForDisplayed()
|
||||
await fileItem.click()
|
||||
|
||||
const uploadButton = await browser.$('.uppy-ProviderBrowser-footer .uppy-u-reset.uppy-c-btn.uppy-c-btn-primary')
|
||||
await uploadButton.click()
|
||||
const retryButton = await browser.$('.uppy-StatusBar-actionBtn--retry')
|
||||
await retryButton.waitForDisplayed(10000)
|
||||
await retryButton.click()
|
||||
|
||||
const completeBar = await browser.$('.uppy-StatusBar-content[title="Complete"]')
|
||||
await completeBar.waitForDisplayed(20000)
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy Providers</h2>
|
||||
<div>
|
||||
<div id="uppyDashboard"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import GoogleDrive from '@uppy/google-drive'
|
||||
import Instagram from '@uppy/instagram'
|
||||
import Dropbox from '@uppy/dropbox'
|
||||
import Box from '@uppy/box'
|
||||
import Tus from '@uppy/tus'
|
||||
|
||||
const companionUrl = 'http://localhost:3020'
|
||||
|
||||
window.uppy = new Uppy({
|
||||
id: 'uppyProvider',
|
||||
debug: true,
|
||||
autoProceed: true,
|
||||
})
|
||||
.use(Dashboard, {
|
||||
target: '#uppyDashboard',
|
||||
inline: true,
|
||||
})
|
||||
.use(GoogleDrive, { target: Dashboard, companionUrl })
|
||||
.use(Instagram, { target: Dashboard, companionUrl })
|
||||
.use(Dropbox, { target: Dashboard, companionUrl })
|
||||
.use(Box, { target: Dashboard, companionUrl })
|
||||
.use(Tus, { endpoint: 'https://tusd.tusdemo.net/files/' })
|
||||
|
||||
if (window.location.search === '?socketerr=true') {
|
||||
const emitError = (file, data) => {
|
||||
// trigger fake socket error
|
||||
data.uploader.uploaderSockets[file.id].emit(
|
||||
'error', { error: { message: 'nobody likes me, thats ok' } },
|
||||
)
|
||||
window.uppy.off('upload-progress', emitError)
|
||||
}
|
||||
window.uppy.on('upload-progress', emitError)
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
/* global browser */
|
||||
|
||||
/*
|
||||
WARNING! PLEASE READ THIS BEFORE ENABLING THIS TEST ON TRAVIS.
|
||||
|
||||
Before enabling this test on travis, please keep in mind that with this "no_ssl_bump_domains" option set
|
||||
here https://github.com/transloadit/uppy/blob/998c7b1805acb8d305a562dd9726ebae98575e07/.travis.yml#L33
|
||||
SSL encryption may not be enabled between the running companion and the testing browser client.
|
||||
|
||||
Hence, provider tokens (Google, Instagram, Dropbox) may be at risk of getting hijacked.
|
||||
*/
|
||||
const { finishUploadTest, startUploadTest, uploadWithRetry } = require('./helper')
|
||||
|
||||
const testURL = 'http://localhost:4567/providers'
|
||||
|
||||
describe('File upload with Dropbox Provider', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should upload a file completely with Dropbox', async function test () {
|
||||
if (!process.env.UPPY_GOOGLE_EMAIL) {
|
||||
console.log('skipping Dropbox integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
// ensure session is cleared
|
||||
await startUploadTest(browser, 'Dropbox', /dropbox/)
|
||||
// do oauth authentication
|
||||
const authButton = await browser.$('button.auth-google')
|
||||
await authButton.waitForDisplayed()
|
||||
await authButton.click()
|
||||
await browser.pause(3000)
|
||||
// we login with google to avoid captcha
|
||||
await signIntoGoogle(browser)
|
||||
await browser.pause(5000)
|
||||
// if we dropbox displays a warning about trusting the companion app
|
||||
// we allow it because we trust companion. Companion is our friend.
|
||||
const acceptWarning = await browser.$('#warning-button-continue')
|
||||
if (await acceptWarning.isExisting()) {
|
||||
await acceptWarning.click()
|
||||
}
|
||||
|
||||
await browser.pause(3000)
|
||||
// finish oauth
|
||||
const allowAccessButton = await browser.$('button[name=allow_access]')
|
||||
await allowAccessButton.waitForDisplayed()
|
||||
await allowAccessButton.click()
|
||||
|
||||
await finishUploadTest(browser)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should resume uploads when retry is triggered with Dropbox', async function test () {
|
||||
if (!process.env.UPPY_GOOGLE_EMAIL) {
|
||||
console.log('skipping Dropbox integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
await uploadWithRetry(browser, 'Dropbox', testURL)
|
||||
})
|
||||
})
|
||||
|
||||
const signIntoGoogle = async (browser) => {
|
||||
const emailInput = await browser.$('#identifierId')
|
||||
await emailInput.waitForExist(30000)
|
||||
await emailInput.setValue(process.env.UPPY_GOOGLE_EMAIL)
|
||||
let nextButton = await browser.$('#identifierNext')
|
||||
await nextButton.click()
|
||||
|
||||
const passwordInput = await browser.$('input[name=password]')
|
||||
await passwordInput.waitForDisplayed(30000)
|
||||
await passwordInput.setValue(process.env.UPPY_GOOGLE_PASSWORD)
|
||||
nextButton = await browser.$('#passwordNext')
|
||||
await nextButton.click()
|
||||
await browser.pause(3000)
|
||||
|
||||
const emailListItem = await browser.$(`li div[data-identifier="${process.env.UPPY_GOOGLE_EMAIL}"]`)
|
||||
if (await emailListItem.isExisting()) {
|
||||
// if user is already signed in, just select user
|
||||
await emailListItem.click()
|
||||
}
|
||||
|
||||
const allowDropboxButton = await browser.$('#submit_approve_access')
|
||||
if (await allowDropboxButton.isExisting()) {
|
||||
// if dropbox has never been allowed, allow it
|
||||
await allowDropboxButton.click()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/* global browser */
|
||||
|
||||
/*
|
||||
WARNING! PLEASE READ THIS BEFORE ENABLING THIS TEST ON TRAVIS.
|
||||
|
||||
Before enabling this test on travis, please keep in mind that with this "no_ssl_bump_domains" option set
|
||||
here https://github.com/transloadit/uppy/blob/998c7b1805acb8d305a562dd9726ebae98575e07/.travis.yml#L33
|
||||
SSL encryption may not be enabled between the running companion and the testing browser client.
|
||||
|
||||
Hence, provider tokens (Google, Instagram, Dropbox) may be at risk of getting hijacked.
|
||||
*/
|
||||
const { finishUploadTest, startUploadTest, uploadWithRetry } = require('./helper')
|
||||
|
||||
const testURL = 'http://localhost:4567/providers'
|
||||
|
||||
describe('File upload with Google Drive Provider', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should upload a file completely with Google Drive', async function test () {
|
||||
if (!process.env.UPPY_GOOGLE_EMAIL) {
|
||||
console.log('skipping Google Drive integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
// ensure session is cleared
|
||||
await startUploadTest(browser, 'GoogleDrive', /google/)
|
||||
await signIntoGoogle(browser)
|
||||
await finishUploadTest(browser)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should resume uploads when retry is triggered with Google Drive', async function test () {
|
||||
if (!process.env.UPPY_GOOGLE_EMAIL) {
|
||||
console.log('skipping Google Drive integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
await uploadWithRetry(browser, 'GoogleDrive', testURL)
|
||||
})
|
||||
})
|
||||
|
||||
const signIntoGoogle = async (browser) => {
|
||||
const emailInput = await browser.$('#identifierId')
|
||||
await emailInput.waitForExist(30000)
|
||||
await emailInput.setValue(process.env.UPPY_GOOGLE_EMAIL)
|
||||
let nextButton = await browser.$('#identifierNext')
|
||||
await nextButton.click()
|
||||
|
||||
const passwordInput = await browser.$('input[name=password]')
|
||||
await passwordInput.waitForDisplayed(30000)
|
||||
await passwordInput.setValue(process.env.UPPY_GOOGLE_PASSWORD)
|
||||
nextButton = await browser.$('#passwordNext')
|
||||
await nextButton.click()
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/* global browser */
|
||||
|
||||
/*
|
||||
WARNING! PLEASE READ THIS BEFORE ENABLING THIS TEST ON TRAVIS.
|
||||
|
||||
Before enabling this test on travis, please keep in mind that with this "no_ssl_bump_domains" option set
|
||||
here https://github.com/transloadit/uppy/blob/998c7b1805acb8d305a562dd9726ebae98575e07/.travis.yml#L33
|
||||
SSL encryption may not be enabled between the running companion and the testing browser client.
|
||||
|
||||
Hence, provider tokens (Google, Instagram, Dropbox) may be at risk of getting hijacked.
|
||||
*/
|
||||
const { finishUploadTest, startUploadTest, uploadWithRetry } = require('./helper')
|
||||
|
||||
const testURL = 'http://localhost:4567/providers'
|
||||
|
||||
describe('File upload with Instagram Provider', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should upload a file completely with Instagram', async function test () {
|
||||
if (!process.env.UPPY_INSTAGRAM_USERNAME) {
|
||||
console.log('skipping Instagram integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
// ensure session is cleared
|
||||
await startUploadTest(browser, 'Instagram', /instagram/)
|
||||
// do oauth authentication
|
||||
const usernameInput = await browser.$('input[name=username]')
|
||||
await usernameInput.waitForExist(20000)
|
||||
await usernameInput.setValue(process.env.UPPY_INSTAGRAM_USERNAME)
|
||||
const passwordInput = await browser.$('input[name=password]')
|
||||
await passwordInput.setValue(process.env.UPPY_INSTAGRAM_PASSWORD)
|
||||
const submit = await browser.$('form button[type=submit]')
|
||||
await submit.click()
|
||||
// wait a bit for submission
|
||||
await browser.pause(5000)
|
||||
if ((await browser.getWindowHandles()).length > 1) {
|
||||
// if suspicious login was detected, the window will remain unclosed
|
||||
// so we have to input the security code sent
|
||||
const challengeChoice = await browser.$('input[name="choice"]')
|
||||
if (await challengeChoice.isExisting()) {
|
||||
const acceptChallengButton = await browser.$('form button')
|
||||
await acceptChallengButton.click()
|
||||
|
||||
const securityCodeInput = await browser.$('input[name=security_code]')
|
||||
await securityCodeInput.waitForExist()
|
||||
// we can't automate the submission of security code
|
||||
// because it is sent to the email. So we wait till it is filled manually
|
||||
await securityCodeInput.waitUntil(
|
||||
() => securityCodeInput.getValue(),
|
||||
30_000,
|
||||
'expected security code to be manually entered',
|
||||
)
|
||||
}
|
||||
|
||||
// instagram may ask for auth confirmation to allow companion
|
||||
const allowButton = await browser.$('button[value="Authorize"]')
|
||||
if (await allowButton.isExisting()) {
|
||||
await allowButton.click()
|
||||
}
|
||||
}
|
||||
|
||||
await finishUploadTest(browser)
|
||||
})
|
||||
|
||||
// not using arrow functions as cb so to keep mocha in the 'this' context
|
||||
it('should resume uploads when retry is triggered Instagram', async function test () {
|
||||
if (!process.env.UPPY_INSTAGRAM_USERNAME) {
|
||||
console.log('skipping Instagram integration test')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
await uploadWithRetry(browser, 'Instagram', testURL)
|
||||
})
|
||||
})
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Thumbnails</h2>
|
||||
<div>
|
||||
<div id="uppyThumbnails"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import ThumbnailGenerator from '@uppy/thumbnail-generator'
|
||||
import FileInput from '@uppy/file-input'
|
||||
|
||||
const uppyThumbnails = new Uppy({
|
||||
id: 'uppyThumbnails',
|
||||
autoProceed: false,
|
||||
debug: true,
|
||||
})
|
||||
|
||||
uppyThumbnails.use(ThumbnailGenerator, {})
|
||||
uppyThumbnails.use(FileInput, { target: '#uppyThumbnails', pretty: false })
|
||||
|
||||
uppyThumbnails.on('file-added', (file) => {
|
||||
const el = document.createElement('p')
|
||||
el.className = 'file-name'
|
||||
el.textContent = file.name
|
||||
document.body.appendChild(el)
|
||||
})
|
||||
|
||||
// Dump errors to the screen so saucelabs shows them in screenshots.
|
||||
uppyThumbnails.on('thumbnail:error', (file, err) => {
|
||||
const el = document.createElement('pre')
|
||||
el.style = 'font: 14pt monospace; background: red; color: white'
|
||||
el.textContent = `Error: ${err.stack}`
|
||||
document.body.appendChild(el)
|
||||
})
|
||||
|
||||
uppyThumbnails.on('thumbnail:generated', (file) => {
|
||||
const img = new Image()
|
||||
img.src = file.preview
|
||||
img.className = 'file-preview'
|
||||
img.style.display = 'block'
|
||||
|
||||
document.body.appendChild(img)
|
||||
})
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
/* global browser, capabilities, $, $$ */
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { selectFakeFile, supportsChooseFile } = require('../utils')
|
||||
|
||||
const testURL = 'http://localhost:4567/thumbnails'
|
||||
|
||||
const images = [
|
||||
path.join(__dirname, '../../resources/image.jpg'),
|
||||
path.join(__dirname, '../../resources/baboon.png'),
|
||||
path.join(__dirname, '../../resources/kodim23.png'),
|
||||
path.join(__dirname, '../../resources/invalid.png'),
|
||||
]
|
||||
const notImages = [
|
||||
{ type: 'text/javascript', file: __filename },
|
||||
]
|
||||
|
||||
describe('ThumbnailGenerator', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should generate thumbnails for images', async function test () {
|
||||
// Does not work on IE right now
|
||||
if (capabilities.browserName === 'internet explorer') {
|
||||
this.skip()
|
||||
return
|
||||
}
|
||||
|
||||
const input = await $('#uppyThumbnails .uppy-FileInput-input')
|
||||
await input.waitForExist()
|
||||
|
||||
await browser.execute(/* must be valid ES5 for IE */ () => {
|
||||
window.thumbnailsReady = new Promise((resolve) => {
|
||||
window.uppyThumbnails.on('thumbnail:all-generated', resolve)
|
||||
})
|
||||
})
|
||||
|
||||
if (supportsChooseFile()) {
|
||||
for (const file of images) {
|
||||
await input.setValue(file)
|
||||
}
|
||||
for (const { file } of notImages) {
|
||||
await input.setValue(file)
|
||||
}
|
||||
} else {
|
||||
for (const img of images) {
|
||||
await browser.execute(
|
||||
selectFakeFile,
|
||||
'uppyThumbnails',
|
||||
path.basename(img), // name
|
||||
`image/${path.extname(img).slice(1)}`, // type
|
||||
fs.readFileSync(img, 'base64'), // b64
|
||||
)
|
||||
}
|
||||
for (const { type, file } of notImages) {
|
||||
await browser.execute(
|
||||
selectFakeFile,
|
||||
'uppyThumbnails',
|
||||
path.basename(file), // name
|
||||
type, // type
|
||||
fs.readFileSync(file, 'base64'), // b64
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await browser.executeAsync(/* must be valid ES5 for IE */ (done) => {
|
||||
window.thumbnailsReady.then(done)
|
||||
})
|
||||
|
||||
// const names = $$('p.file-name')
|
||||
const previews = await $$('img.file-preview')
|
||||
|
||||
// Names should all be listed before previews--indicates that previews were generated asynchronously.
|
||||
/* Nevermind this, setValue() doesn't accept multiple files so they are added one by one and the thumbnails
|
||||
* have finished generating by the time we add the next.
|
||||
const nys = names.map((el) => el.getLocation('y'))
|
||||
const pys = previews.map((el) => el.getLocation('y'))
|
||||
for (const ny of nys) {
|
||||
for (const py of pys) {
|
||||
expect(ny).to.be.below(py, 'names should be listed before previews')
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
expect(previews).to.have.lengthOf(3) // ex. the invalid image
|
||||
for (const p of previews) {
|
||||
expect(await p.getAttribute('src')).to.match(/^blob:/)
|
||||
expect(await getWidth(p)).to.equal(200)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function getWidth (ref) {
|
||||
try {
|
||||
return await ref.getSize('width')
|
||||
} catch (err) {
|
||||
return browser.execute((el) => {
|
||||
return el.getBoundingClientRect().width
|
||||
}, ref)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy Transloadit</h2>
|
||||
<div id="uppy-transloadit"></div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Transloadit from '@uppy/transloadit'
|
||||
|
||||
function initUppyTransloadit (transloaditKey) {
|
||||
const uppyTransloadit = new Uppy({
|
||||
id: 'uppyTransloadit',
|
||||
debug: true,
|
||||
autoProceed: true,
|
||||
})
|
||||
|
||||
uppyTransloadit
|
||||
.use(Dashboard, {
|
||||
target: '#uppy-transloadit',
|
||||
inline: true,
|
||||
})
|
||||
.use(Transloadit, {
|
||||
service: 'https://api2-ap-southeast-1.transloadit.com',
|
||||
getAssemblyOptions () {
|
||||
return {
|
||||
params: {
|
||||
auth: { key: transloaditKey },
|
||||
steps: {
|
||||
crop_thumbed: {
|
||||
use: [':original'],
|
||||
robot: '/image/resize',
|
||||
height: 100,
|
||||
resize_strategy: 'crop',
|
||||
width: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
message: 'test',
|
||||
},
|
||||
}
|
||||
},
|
||||
waitForEncoding: true,
|
||||
})
|
||||
|
||||
uppyTransloadit.on('transloadit:result', (_, __, assembly) => {
|
||||
// use transloadit encoding result here.
|
||||
const result = document.createElement('div')
|
||||
result.setAttribute('id', 'uppy-result')
|
||||
result.textContent = assembly.fields.message === 'test' ? 'ok' : 'fail'
|
||||
document.body.appendChild(result)
|
||||
})
|
||||
}
|
||||
|
||||
window.initUppyTransloadit = initUppyTransloadit
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/* global browser, capabilities, $ */
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { selectFakeFile, supportsChooseFile, ensureInputVisible } = require('../utils')
|
||||
|
||||
const testURL = 'http://localhost:4567/transloadit-assembly-options'
|
||||
|
||||
function setTransloaditKeyAndInit (transloaditKey) {
|
||||
window.initUppyTransloadit(transloaditKey)
|
||||
}
|
||||
|
||||
describe('Transloadit getAssemblyOptions', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should pass `fields` to `assembly:result`', async function test () {
|
||||
const transloaditKey = process.env.TRANSLOADIT_KEY
|
||||
if (transloaditKey === undefined) {
|
||||
console.log('skipping Transloadit integration test: process.env.TRANSLOADIT_KEY is not set')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
const wrapper = await $('#uppy-transloadit')
|
||||
await wrapper.waitForExist()
|
||||
|
||||
await browser.execute(setTransloaditKeyAndInit, transloaditKey)
|
||||
|
||||
const input = await $('#uppy-transloadit .uppy-Dashboard-input')
|
||||
const result = await $('#uppy-result')
|
||||
|
||||
await input.waitForExist()
|
||||
await browser.execute(ensureInputVisible, '#uppy-transloadit .uppy-Dashboard-input')
|
||||
|
||||
if (supportsChooseFile(capabilities)) {
|
||||
await input.setValue(path.join(__dirname, '../../resources/image.jpg'))
|
||||
} else {
|
||||
const img = path.join(__dirname, '../../resources/image.jpg')
|
||||
await browser.execute(
|
||||
selectFakeFile,
|
||||
'uppyTransloadit',
|
||||
path.basename(img), // name
|
||||
'image/jpeg', // type
|
||||
fs.readFileSync(img, 'base64'), // b64
|
||||
)
|
||||
}
|
||||
await result.waitForExist({ timeout: 25000 })
|
||||
const text = await result.getText()
|
||||
expect(text).to.be.equal('ok')
|
||||
})
|
||||
})
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy Transloadit</h2>
|
||||
<div id="uppy-transloadit"></div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Transloadit from '@uppy/transloadit'
|
||||
|
||||
function initUppyTransloadit (transloaditKey) {
|
||||
const uppyTransloadit = new Uppy({
|
||||
id: 'uppyTransloadit',
|
||||
debug: true,
|
||||
autoProceed: true,
|
||||
})
|
||||
|
||||
uppyTransloadit
|
||||
.use(Dashboard, {
|
||||
target: '#uppy-transloadit',
|
||||
inline: true,
|
||||
})
|
||||
.use(Transloadit, {
|
||||
service: 'https://api2-ap-southeast-1.transloadit.com',
|
||||
params: {
|
||||
auth: { key: transloaditKey },
|
||||
steps: {
|
||||
crop_thumbed: {
|
||||
use: [':original'],
|
||||
robot: '/image/resize',
|
||||
height: 100,
|
||||
resize_strategy: 'crop',
|
||||
width: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
waitForEncoding: true,
|
||||
})
|
||||
|
||||
uppyTransloadit.on('transloadit:result', (stepName, result) => {
|
||||
// use transloadit encoding result here.
|
||||
const img = new Image()
|
||||
img.onload = function onload () {
|
||||
const resultDiv = document.createElement('div')
|
||||
resultDiv.setAttribute('id', 'uppy-result')
|
||||
resultDiv.textContent = 'ok'
|
||||
document.body.appendChild(resultDiv)
|
||||
}
|
||||
img.src = result.url
|
||||
})
|
||||
}
|
||||
|
||||
window.initUppyTransloadit = initUppyTransloadit
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/* global browser, capabilities, $ */
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const { selectFakeFile, supportsChooseFile, ensureInputVisible } = require('../utils')
|
||||
|
||||
const testURL = 'http://localhost:4567/transloadit'
|
||||
|
||||
function setTransloaditKeyAndInit (transloaditKey) {
|
||||
window.initUppyTransloadit(transloaditKey)
|
||||
}
|
||||
|
||||
describe('Transloadit file processing', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.url(testURL)
|
||||
})
|
||||
|
||||
it('should upload a file to Transloadit and crop it', async function test () {
|
||||
const transloaditKey = process.env.TRANSLOADIT_KEY
|
||||
if (transloaditKey === undefined) {
|
||||
console.log('skipping Transloadit integration test: process.env.TRANSLOADIT_KEY is not set')
|
||||
return this.skip()
|
||||
}
|
||||
|
||||
const wrapper = await $('#uppy-transloadit')
|
||||
await wrapper.waitForExist()
|
||||
|
||||
await browser.execute(setTransloaditKeyAndInit, transloaditKey)
|
||||
|
||||
const input = await $('#uppy-transloadit .uppy-Dashboard-input')
|
||||
const result = await $('#uppy-result')
|
||||
|
||||
await input.waitForExist()
|
||||
await browser.execute(ensureInputVisible, '#uppy-transloadit .uppy-Dashboard-input')
|
||||
|
||||
if (supportsChooseFile(capabilities)) {
|
||||
await input.setValue(path.join(__dirname, '../../resources/image.jpg'))
|
||||
} else {
|
||||
const img = path.join(__dirname, '../../resources/image.jpg')
|
||||
await browser.execute(
|
||||
selectFakeFile,
|
||||
'uppyTransloadit',
|
||||
path.basename(img), // name
|
||||
'image/jpeg', // type
|
||||
fs.readFileSync(img, 'base64'), // b64
|
||||
)
|
||||
}
|
||||
await result.waitForExist({ timeout: 25000 })
|
||||
const text = await result.getText()
|
||||
expect(text).to.be.equal('ok')
|
||||
})
|
||||
})
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy Dashboard + Tus</h2>
|
||||
<div>
|
||||
<div id="uppyDashboard"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
import Uppy from '@uppy/core'
|
||||
import Dashboard from '@uppy/dashboard'
|
||||
import Tus from '@uppy/tus'
|
||||
|
||||
const endpoint = 'http://localhost:1080'
|
||||
|
||||
const uppyDashboard = new Uppy({
|
||||
id: 'uppyDashboard',
|
||||
debug: true,
|
||||
})
|
||||
|
||||
uppyDashboard
|
||||
.use(Dashboard, {
|
||||
target: '#uppyDashboard',
|
||||
inline: true,
|
||||
})
|
||||
.use(Tus, { endpoint: `${endpoint}/files/` })
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Uppy test page</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>onerror = function (message, source, line, col, error) {
|
||||
document.body.appendChild(document.createTextNode(message))
|
||||
document.body.appendChild(document.createTextNode(source))
|
||||
document.body.appendChild(document.createTextNode(line + ':' + col))
|
||||
}</script>
|
||||
<style>
|
||||
main {
|
||||
max-width: 700px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#uppyDragDrop-progress {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<main>
|
||||
<h2>Uppy DragDrop + Tus</h2>
|
||||
<div>
|
||||
<div id="uppyDragDrop"></div>
|
||||
<div id="uppyDragDrop-progress"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<link href="uppy.min.css" rel="stylesheet">
|
||||
<script src="bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||