Skip to content

Commit

Permalink
@uppy/transloadit: simplify plugin to always run a single assembly (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Murderlon authored Jun 21, 2024
1 parent 4625dae commit 521d903
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 792 deletions.
231 changes: 54 additions & 177 deletions e2e/cypress/integration/dashboard-transloadit.spec.ts
Original file line number Diff line number Diff line change
@@ -1,215 +1,86 @@
import Uppy from '@uppy/core'
import Transloadit from '@uppy/transloadit'

function getPlugin<M = any, B = any>(uppy: Uppy<M, B>) {
return uppy.getPlugin<Transloadit<M, B>>('Transloadit')!
}

describe('Dashboard with Transloadit', () => {
beforeEach(() => {
cy.visit('/dashboard-transloadit')
cy.get('.uppy-Dashboard-input:first').as('file-input')
cy.intercept('/assemblies').as('createAssemblies')
cy.intercept('/assemblies/*').as('assemblies')
cy.intercept('/resumable/*').as('resumable')
})

it('should upload cat image successfully', () => {
cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', {
force: true,
})

cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait(['@assemblies', '@resumable']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})
it('should upload all files as a single assembly with UppyFile metadata in Upload-Metadata', () => {
cy.intercept({ path: '/assemblies', method: 'POST' }).as('createAssembly')

it('should close assembly polling when cancelled', () => {
cy.intercept({
method: 'GET',
url: '/assemblies/*',
}).as('assemblyPolling')
cy.intercept(
{ method: 'DELETE', pathname: '/assemblies/*', times: 1 },
{ statusCode: 204, body: {} },
).as('delete')
cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
'cypress/fixtures/images/traffic.jpg',
],
{ force: true },
)

cy.window().then(({ uppy }) => {
cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
'cypress/fixtures/images/traffic.jpg',
'cypress/fixtures/images/car.jpg',
],
{ force: true },
)
cy.get('.uppy-StatusBar-actionBtn--upload').click()
// Set metadata on all files
uppy.setMeta({ sharedMetaProperty: 'bar' })
const [file1, file2] = uppy.getFiles()
// Set unique metdata per file as before that's how we determined to create multiple assemblies
uppy.setFileMeta(file1.id, { one: 'one' })
uppy.setFileMeta(file2.id, { two: 'two' })

cy.wait(['@createAssemblies']).then(() => {
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every(
(a: any) => a.pollInterval,
),
).to.equal(true)

uppy.cancelAll()
cy.get('.uppy-StatusBar-actionBtn--upload').click()

cy.wait(['@delete']).then(() => {
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some(
(a: any) => a.pollInterval,
),
).to.equal(false)
})
cy.intercept('POST', '/resumable/*', (req) => {
expect(req.headers['upload-metadata']).to.include('sharedMetaProperty')
req.continue()
})
})
})

// Too flaky at the moment. Arguably, this is not the right place
// as this is doing white box testing (testing internal state).
// But E2e is more about black box testing, you don’t care about the internals, only the result.
// May make more sense to turn this into a unit test.
it.skip('should emit one assembly-cancelled event when cancelled', () => {
const spy = cy.spy()

cy.window().then(({ uppy }) => {
// eslint-disable-next-line
// @ts-ignore fix me
uppy.on('transloadit:assembly-cancelled', spy)

cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
'cypress/fixtures/images/traffic.jpg',
],
{ force: true },
)

cy.intercept({
method: 'GET',
url: '/assemblies/*',
}).as('assemblyPolling')
cy.intercept(
{ method: 'PATCH', pathname: '/files/*', times: 1 },
{ statusCode: 204, body: {} },
)
cy.intercept(
{ method: 'DELETE', pathname: '/resumable/files/*', times: 2 },
{ statusCode: 204, body: {} },
).as('fileDeletion')
cy.intercept({
method: 'DELETE',
pathname: '/assemblies/*',
times: 1,
}).as('assemblyDeletion')

cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait('@assemblyPolling')
cy.get('button[data-cy=cancel]').click()
cy.wait('@assemblyDeletion')
// Unfortunately, waiting on a network request somehow often results in a race condition.
// We just want to know wether this is called or not, so waiting for 2 sec to be sure.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000)
expect(spy).to.be.calledOnce
cy.wait(['@createAssembly']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
// should only create one assembly
cy.get('@createAssembly.all').should('have.length', 1)
})
})
})

it.skip('should close assembly polling when all files are removed', () => {
const spy = cy.spy()
it('should close assembly when cancelled', () => {
cy.intercept({ path: '/resumable/*', method: 'POST' }).as('tusCreate')
cy.intercept({ path: '/assemblies', method: 'POST' }).as('createAssemblies')
cy.intercept({ path: '/assemblies/*', method: 'DELETE' }).as('delete')
cy.intercept({ path: '/resumable/files/*', method: 'DELETE' }).as(
'tusDelete',
)

cy.window().then(({ uppy }) => {
// eslint-disable-next-line
// @ts-ignore fix me
uppy.on('transloadit:assembly-cancelled', spy)

cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
'cypress/fixtures/images/traffic.jpg',
'cypress/fixtures/images/car.jpg',
],
{ force: true },
)

cy.intercept({
method: 'GET',
url: '/assemblies/*',
}).as('assemblyPolling')
cy.intercept(
{ method: 'PATCH', pathname: '/files/*', times: 1 },
{ statusCode: 204, body: {} },
)
cy.intercept(
{ method: 'DELETE', pathname: '/resumable/files/*', times: 2 },
{ statusCode: 204, body: {} },
).as('fileDeletion')
cy.intercept({
method: 'DELETE',
pathname: '/assemblies/*',
times: 1,
}).as('assemblyDeletion')

cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait('@assemblyPolling')
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every(
(a: any) => a.pollInterval,
),
).to.equal(true)

const { files } = uppy.getState()
// eslint-disable-next-line
// @ts-ignore fix me
uppy.removeFiles(Object.keys(files))

cy.wait('@assemblyDeletion').then(() => {
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some(
(a: any) => a.pollInterval,
),
).to.equal(false)
expect(spy).to.be.calledOnce
})
})
})
cy.wait(['@createAssemblies', '@tusCreate']).then(() => {
const plugin = getPlugin(uppy)

it('should not create assembly when all individual files have been cancelled', () => {
cy.window().then(({ uppy }) => {
cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
'cypress/fixtures/images/traffic.jpg',
],
{ force: true },
)
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).length,
).to.equal(0)
expect(plugin.assembly.closed).to.be.false

cy.get('.uppy-StatusBar-actionBtn--upload').click()
uppy.cancelAll()

const { files } = uppy.getState()
// eslint-disable-next-line
// @ts-ignore fix me
uppy.removeFiles(Object.keys(files))

cy.wait('@createAssemblies').then(() => {
// eslint-disable-next-line
// @ts-ignore fix me
expect(
Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some(
(a: any) => a.pollInterval,
),
).to.equal(false)
cy.wait(['@delete', '@tusDelete']).then(() => {
expect(plugin.assembly.closed).to.be.true
})
})
})
})

it('should not emit error if upload is cancelled right away', () => {
cy.intercept({ path: '/assemblies', method: 'POST' }).as('createAssemblies')

cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', {
force: true,
})
Expand Down Expand Up @@ -444,6 +315,9 @@ describe('Dashboard with Transloadit', () => {
})

it('should complete on retry', () => {
cy.intercept('/assemblies/*').as('assemblies')
cy.intercept('/resumable/*').as('resumable')

cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
Expand Down Expand Up @@ -473,6 +347,9 @@ describe('Dashboard with Transloadit', () => {
})

it('should complete when resuming after pause', () => {
cy.intercept({ path: '/assemblies', method: 'POST' }).as('createAssemblies')
cy.intercept('/resumable/*').as('resumable')

cy.get('@file-input').selectFile(
[
'cypress/fixtures/images/cat.jpg',
Expand Down
2 changes: 1 addition & 1 deletion e2e/cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ import type { Uppy } from '@uppy/core'

declare global {
interface Window {
uppy: Uppy
uppy: Uppy<any, any>
}
}
Loading

0 comments on commit 521d903

Please sign in to comment.