mirror of
https://github.com/transloadit/uppy.git
synced 2026-07-17 16:50:22 +00:00
- This PR upgrades `biome` from `2.0.5` -> `2.1.2` and adds two new rules - [noUnusedImports](https://biomejs.dev/linter/rules/no-unused-private-class-members) ( we already had suppressions for this rule in code, but the rule itself was never enabled in the config ) - [noUnusedPrivateClassMembers](https://biomejs.dev/linter/rules/no-unused-private-class-members/) - remove stale suppressions. - remove stale code. --------- Co-authored-by: Mikael Finstad <finstaden@gmail.com>
581 lines
17 KiB
TypeScript
581 lines
17 KiB
TypeScript
import nock from 'nock'
|
|
import request from 'supertest'
|
|
import {
|
|
afterAll,
|
|
afterEach,
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
test,
|
|
vi,
|
|
} from 'vitest'
|
|
import * as tokenService from '../src/server/helpers/jwt.js'
|
|
import { isRecord } from '../src/server/helpers/type-guards.js'
|
|
import * as providerModule from '../src/server/provider/index.js'
|
|
import * as defaults from './fixtures/constants.js'
|
|
import { nockGoogleDownloadFile } from './fixtures/drive.js'
|
|
import * as fixtures from './fixtures/index.js'
|
|
import {
|
|
nockZoomRecordings,
|
|
nockZoomRevoke,
|
|
expects as zoomExpects,
|
|
} from './fixtures/zoom.js'
|
|
import mockOauthState from './mockoauthstate.js'
|
|
import { getServer } from './mockserver.js'
|
|
|
|
const { localZoomKey, localZoomSecret } = zoomExpects
|
|
|
|
vi.mock('express-prom-bundle')
|
|
vi.mock('tus-js-client')
|
|
|
|
mockOauthState()
|
|
|
|
vi.mock('../../src/server/helpers/request.js', () => {
|
|
return {
|
|
getURLMeta: () => Promise.resolve({ size: 758051 }),
|
|
}
|
|
})
|
|
|
|
const getServerWithEnv = async () =>
|
|
getServer({ COMPANION_CLIENT_SOCKET_CONNECT_TIMEOUT: '0' })
|
|
const OAUTH_STATE = 'some-cool-nice-encrytpion'
|
|
|
|
const secret = 'secret'
|
|
|
|
const providers = providerModule.getDefaultProviders()
|
|
const fixtureProviders = fixtures.providers
|
|
|
|
const providerNames = Object.keys(providers)
|
|
const oauthProviders: Record<string, string> = Object.fromEntries(
|
|
Object.entries(providers).flatMap(([name, provider]) =>
|
|
provider.oauthProvider != null ? [[name, provider.oauthProvider]] : [],
|
|
),
|
|
)
|
|
const authData: Record<string, { accessToken: string }> = {}
|
|
providerNames.forEach((provider) => {
|
|
authData[provider] = { accessToken: 'token value' }
|
|
})
|
|
const token = tokenService.generateEncryptedAuthToken(authData, secret)
|
|
|
|
const thisOrThat = <T>(value1: T | undefined, value2: T): T => {
|
|
if (value1 !== undefined) {
|
|
return value1
|
|
}
|
|
|
|
return value2
|
|
}
|
|
|
|
function nockGetCurrentAccount(times = 1) {
|
|
nock('https://api.dropboxapi.com')
|
|
.post('/2/users/get_current_account')
|
|
.times(times)
|
|
.reply(200, {
|
|
name: {
|
|
given_name: 'Franz',
|
|
surname: 'Ferdinand',
|
|
familiar_name: 'Franz',
|
|
display_name: 'Franz Ferdinand (Personal)',
|
|
abbreviated_name: 'FF',
|
|
},
|
|
email: defaults.USERNAME,
|
|
email_verified: true,
|
|
disabled: false,
|
|
locale: 'en',
|
|
referral_link: 'https://db.tt/ZITNuhtI',
|
|
is_paired: true,
|
|
})
|
|
}
|
|
|
|
beforeEach(() => {
|
|
const url = new URL(defaults.THUMBNAIL_URL)
|
|
nock(url.origin)
|
|
.get(url.pathname)
|
|
.reply(200, () => '')
|
|
})
|
|
|
|
afterEach(() => {
|
|
nock.cleanAll()
|
|
})
|
|
afterAll(() => {
|
|
nock.restore()
|
|
})
|
|
|
|
describe('list provider files', () => {
|
|
async function runTest(providerName: keyof typeof fixtureProviders) {
|
|
const providerFixture = fixtureProviders[providerName]?.expects ?? {}
|
|
const listPath =
|
|
'listPath' in providerFixture ? providerFixture.listPath : ''
|
|
return request(await getServerWithEnv())
|
|
.get(`/${providerName}/list/${listPath}`)
|
|
.set('uppy-auth-token', token)
|
|
.expect(200)
|
|
.then((res) => {
|
|
expect(res.header['i-am']).toBe('http://localhost:3020')
|
|
|
|
return {
|
|
username: res.body.username,
|
|
items: Array.isArray(res.body.items)
|
|
? res.body.items.filter(isRecord)
|
|
: [],
|
|
providerFixture,
|
|
}
|
|
})
|
|
}
|
|
|
|
function expect1({
|
|
username,
|
|
items,
|
|
providerFixture,
|
|
}: {
|
|
username: unknown
|
|
items: Array<Record<string, unknown>>
|
|
providerFixture: (typeof fixtureProviders)[keyof typeof fixtureProviders]['expects']
|
|
}) {
|
|
expect(username).toBe(defaults.USERNAME)
|
|
|
|
const item = items[0]
|
|
if (!item) throw new Error('Expected at least one item')
|
|
|
|
const fixtureItemName =
|
|
'itemName' in providerFixture ? providerFixture.itemName : undefined
|
|
const fixtureItemMimeType =
|
|
'itemMimeType' in providerFixture
|
|
? providerFixture['itemMimeType']
|
|
: undefined
|
|
const fixtureItemId =
|
|
'itemId' in providerFixture ? providerFixture['itemId'] : undefined
|
|
const fixtureItemSize =
|
|
'itemSize' in providerFixture ? providerFixture.itemSize : undefined
|
|
const fixtureItemRequestPath =
|
|
'itemRequestPath' in providerFixture
|
|
? providerFixture['itemRequestPath']
|
|
: undefined
|
|
const fixtureItemIcon =
|
|
'itemIcon' in providerFixture ? providerFixture['itemIcon'] : undefined
|
|
|
|
expect(item['isFolder']).toBe(false)
|
|
expect(item['name']).toBe(fixtureItemName ?? defaults.ITEM_NAME)
|
|
expect(item['mimeType']).toBe(fixtureItemMimeType ?? defaults.MIME_TYPE)
|
|
expect(item['id']).toBe(fixtureItemId ?? defaults.ITEM_ID)
|
|
expect(item['size']).toBe(thisOrThat(fixtureItemSize, defaults.FILE_SIZE))
|
|
expect(item['requestPath']).toBe(fixtureItemRequestPath ?? defaults.ITEM_ID)
|
|
expect(item['icon']).toBe(fixtureItemIcon ?? defaults.THUMBNAIL_URL)
|
|
}
|
|
|
|
test('dropbox', async () => {
|
|
nockGetCurrentAccount()
|
|
nock('https://api.dropboxapi.com')
|
|
.post('/2/files/list_folder')
|
|
.reply(200, {
|
|
entries: [
|
|
{
|
|
'.tag': 'file',
|
|
name: defaults.ITEM_NAME,
|
|
id: defaults.ITEM_ID,
|
|
client_modified: '2015-05-12T15:50:38Z',
|
|
server_modified: '2015-05-12T15:50:38Z',
|
|
rev: 'a1c10ce0dd78',
|
|
size: defaults.FILE_SIZE,
|
|
path_lower: '/homework/math/prime_numbers.txt',
|
|
path_display: '/Homework/math/Prime_Numbers.txt',
|
|
is_downloadable: true,
|
|
has_explicit_shared_members: false,
|
|
content_hash: 'e3b0c44298fc1c149afbf41e4649b934ca49',
|
|
file_lock_info: {
|
|
is_lockholder: true,
|
|
lockholder_name: 'Imaginary User',
|
|
created: '2015-05-12T15:50:38Z',
|
|
},
|
|
},
|
|
],
|
|
cursor: 'ZtkX9_EHj3x7PMkVuFIhwKYXEpwpLwyxp9vMKomUhllil9q7eWiAu',
|
|
has_more: false,
|
|
})
|
|
|
|
const { username, items, providerFixture } = await runTest('dropbox')
|
|
expect1({ username, items, providerFixture })
|
|
})
|
|
|
|
test('box', async () => {
|
|
nock('https://api.box.com').get('/2.0/users/me').reply(200, {
|
|
login: defaults.USERNAME,
|
|
})
|
|
nock('https://api.box.com')
|
|
.get(
|
|
'/2.0/folders/0/items?fields=id%2Cmodified_at%2Cname%2Cpermissions%2Csize%2Ctype&limit=1000',
|
|
)
|
|
.reply(200, {
|
|
entries: [
|
|
{
|
|
type: 'file',
|
|
name: defaults.ITEM_NAME,
|
|
id: defaults.ITEM_ID,
|
|
modified_at: '2015-05-12T15:50:38Z',
|
|
size: defaults.FILE_SIZE,
|
|
},
|
|
],
|
|
})
|
|
|
|
const { username, items, providerFixture } = await runTest('box')
|
|
expect1({ username, items, providerFixture })
|
|
})
|
|
|
|
test('drive', async () => {
|
|
nock('https://www.googleapis.com')
|
|
.get('/drive/v3/drives?fields=*&pageToken=&pageSize=100')
|
|
.reply(200, {
|
|
kind: 'drive#driveList',
|
|
drives: [],
|
|
})
|
|
|
|
nock('https://www.googleapis.com')
|
|
.get(
|
|
'/drive/v3/files?fields=kind%2CnextPageToken%2CincompleteSearch%2Cfiles%28kind%2Cid%2CimageMediaMetadata%2Cname%2CmimeType%2CownedByMe%2Csize%2CmodifiedTime%2CiconLink%2CthumbnailLink%2CteamDriveId%2CvideoMediaMetadata%2CexportLinks%2CshortcutDetails%28targetId%2CtargetMimeType%29%29&q=%28%27root%27+in+parents%29+and+trashed%3Dfalse&pageSize=1000&orderBy=folder%2Cname&includeItemsFromAllDrives=true&supportsAllDrives=true',
|
|
)
|
|
.reply(200, {
|
|
kind: 'drive#fileList',
|
|
nextPageToken: defaults.NEXT_PAGE_TOKEN,
|
|
files: [
|
|
{
|
|
kind: 'drive#file',
|
|
id: defaults.ITEM_ID,
|
|
name: defaults.ITEM_NAME,
|
|
mimeType: defaults.MIME_TYPE,
|
|
iconLink:
|
|
'https://drive-thirdparty.googleusercontent.com/16/type/video/mp4',
|
|
thumbnailLink: defaults.THUMBNAIL_URL,
|
|
modifiedTime: '2016-07-10T20:00:08.096Z',
|
|
ownedByMe: true,
|
|
permissions: [{ role: 'owner', emailAddress: defaults.USERNAME }],
|
|
size: '758051',
|
|
},
|
|
],
|
|
})
|
|
|
|
nock('https://www.googleapis.com')
|
|
.get((uri) => uri.includes('about'))
|
|
.reply(200, { user: { emailAddress: 'john.doe@transloadit.com' } })
|
|
|
|
const { username, items, providerFixture } = await runTest('drive')
|
|
|
|
// Drive has a virtual "shared-with-me" folder as the first item
|
|
const [item0, ...rest] = items
|
|
expect(item0.isFolder).toBe(true)
|
|
expect(item0.name).toBe('Shared with me')
|
|
expect(item0.mimeType).toBe('application/vnd.google-apps.folder')
|
|
expect(item0.id).toBe('shared-with-me')
|
|
expect(item0.requestPath).toBe('shared-with-me')
|
|
expect(item0.icon).toBe('folder')
|
|
|
|
expect1({ username, items: rest, providerFixture })
|
|
})
|
|
|
|
test('facebook', async () => {
|
|
nock('https://graph.facebook.com')
|
|
.post(
|
|
'/',
|
|
[
|
|
'access_token=token+value',
|
|
'appsecret_proof=ee28d8152093b877f193f5fe84a34544ec27160e7f34c7645d02930b3fa95160',
|
|
`batch=${encodeURIComponent('[{"method":"GET","relative_url":"me?fields=email"},{"method":"GET","relative_url":"ALBUM-ID/photos?fields=icon%2Cimages%2Cname%2Cwidth%2Cheight%2Ccreated_time"}]')}`,
|
|
].join('&'),
|
|
)
|
|
.reply(200, [
|
|
{
|
|
code: 200,
|
|
body: JSON.stringify({
|
|
name: 'Fiona Fox',
|
|
birthday: '01/01/1985',
|
|
email: defaults.USERNAME,
|
|
}),
|
|
},
|
|
{
|
|
code: 200,
|
|
body: JSON.stringify({
|
|
data: [
|
|
{
|
|
images: [
|
|
{
|
|
height: 1365,
|
|
source: defaults.THUMBNAIL_URL,
|
|
width: 2048,
|
|
},
|
|
],
|
|
width: 720,
|
|
height: 479,
|
|
created_time: '2015-07-17T17:26:50+0000',
|
|
id: defaults.ITEM_ID,
|
|
},
|
|
],
|
|
paging: {},
|
|
}),
|
|
},
|
|
])
|
|
|
|
const { username, items, providerFixture } = await runTest('facebook')
|
|
expect1({ username, items, providerFixture })
|
|
})
|
|
|
|
test('onedrive', async () => {
|
|
nock('https://graph.microsoft.com').get('/v1.0/me').reply(200, {
|
|
userPrincipalName: defaults.USERNAME,
|
|
mail: defaults.USERNAME,
|
|
})
|
|
nock('https://graph.microsoft.com')
|
|
.get('/v1.0/me/drive/root/children?%24expand=thumbnails&%24top=999')
|
|
.reply(200, {
|
|
value: [
|
|
{
|
|
createdDateTime: '2020-01-31T15:40:26.197Z',
|
|
id: defaults.ITEM_ID,
|
|
lastModifiedDateTime: '2020-01-31T15:40:38.723Z',
|
|
name: defaults.ITEM_NAME,
|
|
size: defaults.FILE_SIZE,
|
|
parentReference: {
|
|
driveId: 'DUMMY-DRIVE-ID',
|
|
driveType: 'personal',
|
|
path: '/drive/root:',
|
|
},
|
|
file: {
|
|
mimeType: defaults.MIME_TYPE,
|
|
},
|
|
thumbnails: [
|
|
{
|
|
id: '0',
|
|
large: {
|
|
height: 452,
|
|
url: defaults.THUMBNAIL_URL,
|
|
width: 800,
|
|
},
|
|
medium: {
|
|
height: 100,
|
|
url: defaults.THUMBNAIL_URL,
|
|
width: 176,
|
|
},
|
|
small: {
|
|
height: 54,
|
|
url: defaults.THUMBNAIL_URL,
|
|
width: 96,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
})
|
|
|
|
const { username, items, providerFixture } = await runTest('onedrive')
|
|
expect1({ username, items, providerFixture })
|
|
})
|
|
|
|
test('zoom', async () => {
|
|
nock('https://zoom.us').get('/v2/users/me').reply(200, {
|
|
id: 'DUMMY-USER-ID',
|
|
first_name: 'John',
|
|
last_name: 'Doe',
|
|
email: 'john.doe@transloadit.com',
|
|
timezone: '',
|
|
dept: '',
|
|
created_at: '2020-07-21T09:13:30Z',
|
|
last_login_time: '2020-10-12T07:55:02Z',
|
|
group_ids: [],
|
|
im_group_ids: [],
|
|
account_id: 'DUMMY-ACCOUNT-ID',
|
|
language: 'en-US',
|
|
})
|
|
nockZoomRecordings()
|
|
|
|
const { username, items, providerFixture } = await runTest('zoom')
|
|
expect1({ username, items, providerFixture })
|
|
})
|
|
})
|
|
|
|
describe('provider file gets downloaded from', () => {
|
|
async function runTest(providerName: keyof typeof fixtureProviders) {
|
|
const providerFixture = fixtureProviders[providerName]?.expects ?? {}
|
|
const requestPath =
|
|
'itemRequestPath' in providerFixture
|
|
? providerFixture.itemRequestPath
|
|
: defaults.ITEM_ID
|
|
const res = await request(await getServerWithEnv())
|
|
.post(`/${providerName}/get/${requestPath}`)
|
|
.set('uppy-auth-token', token)
|
|
.set('Content-Type', 'application/json')
|
|
.send({
|
|
endpoint: 'http://tusd.tusdemo.net/files',
|
|
protocol: 'tus',
|
|
})
|
|
.expect(200)
|
|
|
|
expect(res.body.token).toBeTruthy()
|
|
}
|
|
|
|
test('dropbox', async () => {
|
|
nockGetCurrentAccount(2)
|
|
nock('https://api.dropboxapi.com')
|
|
.post('/2/files/get_metadata')
|
|
.reply(200, { size: defaults.FILE_SIZE })
|
|
nock('https://content.dropboxapi.com')
|
|
.post('/2/files/download')
|
|
.reply(200, {})
|
|
await runTest('dropbox')
|
|
})
|
|
|
|
test('box', async () => {
|
|
nock('https://api.box.com')
|
|
.get(`/2.0/files/${defaults.ITEM_ID}`)
|
|
.reply(200, { size: defaults.FILE_SIZE })
|
|
nock('https://api.box.com')
|
|
.get(`/2.0/files/${defaults.ITEM_ID}/content`)
|
|
.reply(200, { size: defaults.FILE_SIZE })
|
|
await runTest('box')
|
|
})
|
|
|
|
test('drive', async () => {
|
|
nockGoogleDownloadFile()
|
|
await runTest('drive')
|
|
})
|
|
|
|
test('facebook', async () => {
|
|
// times(2) because of size request
|
|
nock('https://graph.facebook.com')
|
|
.post(
|
|
'/',
|
|
[
|
|
'access_token=token+value',
|
|
'appsecret_proof=ee28d8152093b877f193f5fe84a34544ec27160e7f34c7645d02930b3fa95160',
|
|
`batch=${encodeURIComponent('[{"method":"GET","relative_url":"DUMMY-FILE-ID?fields=images"}]')}`,
|
|
].join('&'),
|
|
)
|
|
.times(2)
|
|
.reply(200, [
|
|
{
|
|
code: 200,
|
|
body: JSON.stringify({
|
|
images: [
|
|
{
|
|
height: 1365,
|
|
source: defaults.THUMBNAIL_URL,
|
|
width: 2048,
|
|
},
|
|
],
|
|
id: defaults.ITEM_ID,
|
|
}),
|
|
},
|
|
])
|
|
|
|
await runTest('facebook')
|
|
})
|
|
|
|
test('onedrive', async () => {
|
|
nock('https://graph.microsoft.com')
|
|
.get(`/v1.0/drives/DUMMY-DRIVE-ID/items/${defaults.ITEM_ID}`)
|
|
.reply(200, {
|
|
size: defaults.FILE_SIZE,
|
|
})
|
|
nock('https://graph.microsoft.com')
|
|
.get(`/v1.0/drives/DUMMY-DRIVE-ID/items/${defaults.ITEM_ID}/content`)
|
|
.reply(200, {})
|
|
await runTest('onedrive')
|
|
})
|
|
|
|
test('zoom', async () => {
|
|
// times(2) because of size request
|
|
nockZoomRecordings({ times: 2 })
|
|
nock('https://us02web.zoom.us')
|
|
.get('/rec/download/DUMMY-DOWNLOAD-PATH?access_token=token%20value')
|
|
.reply(200, {})
|
|
await runTest('zoom')
|
|
})
|
|
})
|
|
|
|
describe('connect to provider', () => {
|
|
test.each(
|
|
providerNames,
|
|
)('connect to %s via grant.js endpoint', async (providerName: string) => {
|
|
const oauthProvider = oauthProviders[providerName]
|
|
|
|
if (oauthProvider == null) return
|
|
|
|
await request(await getServerWithEnv())
|
|
.get(`/${providerName}/connect?foo=bar`)
|
|
.set('uppy-auth-token', token)
|
|
.expect(302)
|
|
.expect(
|
|
'Location',
|
|
`http://localhost:3020/connect/${oauthProvider}?state=${OAUTH_STATE}`,
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('logout of provider', () => {
|
|
async function runTest(providerName: string) {
|
|
const res = await request(await getServerWithEnv())
|
|
.get(`/${providerName}/logout/`)
|
|
.set('uppy-auth-token', token)
|
|
.expect(200)
|
|
|
|
// only some providers can actually be revoked
|
|
const expectRevoked = [
|
|
'box',
|
|
'dropbox',
|
|
'drive',
|
|
'facebook',
|
|
'zoom',
|
|
].includes(providerName)
|
|
|
|
expect(res.body).toMatchObject({
|
|
ok: true,
|
|
revoked: expectRevoked,
|
|
})
|
|
}
|
|
|
|
test('dropbox', async () => {
|
|
nockGetCurrentAccount(2)
|
|
nock('https://api.dropboxapi.com')
|
|
.post('/2/auth/token/revoke')
|
|
.reply(200, {})
|
|
await runTest('dropbox')
|
|
})
|
|
|
|
test('box', async () => {
|
|
nock('https://api.box.com').post('/oauth2/revoke').reply(200, {})
|
|
await runTest('box')
|
|
})
|
|
|
|
test('drive', async () => {
|
|
nock('https://accounts.google.com')
|
|
.post('/o/oauth2/revoke?token=token+value')
|
|
.reply(200, {})
|
|
await runTest('drive')
|
|
})
|
|
|
|
test('facebook', async () => {
|
|
// times(2) because of size request
|
|
nock('https://graph.facebook.com')
|
|
.post(
|
|
'/',
|
|
[
|
|
'access_token=token+value',
|
|
'appsecret_proof=ee28d8152093b877f193f5fe84a34544ec27160e7f34c7645d02930b3fa95160',
|
|
`batch=${encodeURIComponent('[{"method":"DELETE","relative_url":"me/permissions"}]')}`,
|
|
].join('&'),
|
|
)
|
|
.reply(200, [
|
|
{
|
|
code: 200,
|
|
body: JSON.stringify({}),
|
|
},
|
|
])
|
|
|
|
await runTest('facebook')
|
|
})
|
|
|
|
test('onedrive', async () => {
|
|
await runTest('onedrive')
|
|
})
|
|
|
|
test('zoom', async () => {
|
|
nockZoomRevoke({ key: localZoomKey, secret: localZoomSecret })
|
|
await runTest('zoom')
|
|
})
|
|
})
|