uppy/packages/@uppy/companion/test/providers.test.ts
Prakash 5519b84409
@uppy: upgrade biome and more improvements (#6244)
- 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>
2026-05-21 12:28:03 +08:00

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')
})
})