Skip to content

Commit

Permalink
test(suite): adapt metadata e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mroz22 committed Oct 20, 2023
1 parent 6dadb68 commit 19858e8
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 32 deletions.
86 changes: 79 additions & 7 deletions packages/e2e-utils/src/mocks/dropbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const port = 30002;
export class DropboxMock {
files: Record<string, any> = {};
nextResponse: Record<string, any>[] = [];
uploadSessionFiles: Record<string, Buffer> = {};
// store requests for assertions in tests
requests: string[] = [];
app?: Express;
Expand Down Expand Up @@ -117,8 +118,8 @@ export class DropboxMock {
metadata: {
'.tag': 'file',
name: query,
path_lower: `/apps/trezor/${query}`,
path_display: `/Apps/TREZOR/${query}`,
path_lower: `/${query}`,
path_display: `/${query}`,
id: 'id:foo-id',
client_modified: '2020-10-07T09:52:45Z',
server_modified: '2020-10-07T09:52:45Z',
Expand All @@ -139,25 +140,85 @@ export class DropboxMock {
res.end();
});

// https://api.dropboxapi.com/2/files/list_folder
app.post('/2/files/list_folder', (_req, res) => {
const entries = Object.keys(this.files).map(name =>
// name is without leading slash /
({ name: name.replace('/', '') }),
);

res.write(
JSON.stringify({
entries,
}),
);

return res.send();
});

// // https://api.dropboxapi.com/2/files/upload_session/start_batch
// app.post('/2/files/upload_session/start_batch', (req, res) => {
// const { num_sessions } = req.body as { num_sessions: number };

// res.write(
// JSON.stringify({
// session_ids: Array.from({ length: num_sessions }, (_, i) => i),
// }),
// );

// return res.send();
// });

// // https://content.dropboxapi.com/2/files/upload_session/append_v2
// app.post('/2/files/upload_session/append_v2', (req, res) => {
// // @ts-expect-error
// const dropboxApiArgs = JSON.parse(req.headers['dropbox-api-arg']);
// const { cursor } = dropboxApiArgs;
// const file = req.body;

// this.uploadSessionFiles[cursor.session_id] = file;

// return res.send();
// });

// // https://content.dropboxapi.com/2/files/upload_session/finish_batch_v2
// app.post('/2/files/upload_session/finish_batch_v2', (req, res) => {
// const { entries } = req.body as {
// entries: Array<{
// cursor: {
// session_id: number;
// };
// commit: {
// path: string;
// };
// }>;
// };

// entries.forEach(({ cursor, commit }) => {
// const file = this.uploadSessionFiles[cursor.session_id];
// this.files[`${commit.path.toLowerCase()}`] = file;
// });

// return res.send();
// });

// https://content.dropboxapi.com/2/files/download
app.post('/2/files/download', (req, res) => {
// @ts-expect-error
const dropboxApiArgs = JSON.parse(req.headers['dropbox-api-arg']);
const { path } = dropboxApiArgs;
const name = path.replace('/apps/trezor', '');

const file = this.files[name];

const file = this.files[path];
if (file) {
// @ts-expect-error
res.writeHeader(200, {
'Content-Type': 'application/octet-stream',
'Dropbox-Api-Result': `{"name": "${name}", "path_lower": "${path}", "path_display": "/Apps/TREZOR/${name}", "id": "id:foo-bar", "client_modified": "2020-10-07T09:52:45Z", "server_modified": "2020-10-07T09:52:45Z", "rev": "foo-bar", "size": 666, "is_downloadable": true, "content_hash": "foo-bar"}`,
'Dropbox-Api-Result': `{"name": "${path}", "path_lower": "${path}", "path_display": "/${path}", "id": "id:foo-bar", "client_modified": "2020-10-07T09:52:45Z", "server_modified": "2020-10-07T09:52:45Z", "rev": "foo-bar", "size": 666, "is_downloadable": true, "content_hash": "foo-bar"}`,
});

res.write(file, 'binary');
} else {
console.error('[dropboxMock]: no such file found', file);
console.error('[dropboxMock]: no such file found', path);
}
return res.end();
});
Expand All @@ -176,6 +237,17 @@ export class DropboxMock {
},
);

// https://content.dropboxapi.com/2/files/upload
app.post(
'/2/files/move_v2',
// express.raw({ type: 'application/octet-stream' }),
(req, res) => {
this.files[req.body.to_path] = this.files[req.body.from_path];
delete this.files[req.body.from_path];
res.send();
},
);

this.app = app;
}

Expand Down
8 changes: 7 additions & 1 deletion packages/e2e-utils/src/mocks/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ class File {
* Mock implementation of Google Drive service intended to be used in e2e tests.
*/
export class GoogleMock {
files: Record<string, any> = {};
nextResponse: Record<string, any>[] = [];
files: Record<string, File> = {};
// store requests for assertions in tests
requests: string[] = [];
app?: Express;
Expand Down Expand Up @@ -154,6 +154,12 @@ export class GoogleMock {
});
});

app.get('/drive/api/v3/reference/files/list', express.json(), (_req, res) => {
res.json({
files: Object.keys(this.files),
});
});

app.get('/drive/v3/about', express.json(), (_req, res) => {
console.log('[mockGoogleDrive]: about');
res.send({
Expand Down
14 changes: 13 additions & 1 deletion packages/suite-web/e2e/cypress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default defineConfig({
addMatchImageSnapshotPlugin(on, config);
});
on('task', {
metadataStartProvider: async provider => {
metadataStartProvider: async (provider: 'dropbox' | 'google') => {
switch (provider) {
case 'dropbox':
await mocked.dropbox.start();
Expand Down Expand Up @@ -141,6 +141,18 @@ export default defineConfig({
throw new Error('not a valid case');
}
},
metadataGetFilesList: ({ provider }) => {
switch (provider) {
case 'dropbox':
return Object.keys(mocked.dropbox.files).map(name =>
name.replace('/apps/trezor/', ''),
);
case 'google':
return Object.keys(mocked.google.files);
default:
throw new Error('not a valid case');
}
},
startMockedBridge: async har => {
await mocked.bridge.start(har);
return null;
Expand Down
2 changes: 0 additions & 2 deletions packages/suite-web/e2e/support/utils/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ export const passThroughBackupShamir = (shares: number, threshold: number) => {
};

export const passThroughInitMetadata = (provider: 'dropbox' | 'google') => {
cy.getConfirmActionOnDeviceModal();
cy.task('pressYes');
cy.getTestElement(`@modal/metadata-provider/${provider}-button`).click();
cy.getTestElement('@modal/metadata-provider').should('not.exist');
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ describe('Dropbox api errors', () => {
// prepare some initial files
cy.task('metadataSetFileContent', {
provider: 'dropbox',
file: '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: '/b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
content: {
version: '1.0.0',
accountLabel: 'already existing label',
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
aesKey: '998daf71f3fbc486076f0ee8d5737a61b82bceacb0ec69100cbe4d45cd79676a',
});
cy.prefixedVisit('/', {
onBeforeLoad: (win: Window) => {
Expand Down Expand Up @@ -80,14 +80,14 @@ describe('Dropbox api errors', () => {
// prepare some initial files
cy.task('metadataSetFileContent', {
provider: 'dropbox',
file: '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: '/b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
content: {
version: '1.0.0',
accountLabel: 'already existing label',
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
aesKey: '998daf71f3fbc486076f0ee8d5737a61b82bceacb0ec69100cbe4d45cd79676a',
});
cy.prefixedVisit('/', {
onBeforeLoad: (win: Window) => {
Expand Down Expand Up @@ -139,6 +139,7 @@ describe('Dropbox api errors', () => {
cy.getTestElement('@suite/menu/wallet-index').click();

cy.getTestElement("@metadata/accountLabel/m/84'/0'/0'").click({ force: true });

cy.getTestElement("@metadata/accountLabel/m/84'/0'/0'/edit-label-button").click({
force: true,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ const fixtures = [
{
provider: 'google',
desc: 'does NOT watch files over time',
file: 'f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: 'b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
content: 'already existing label',
},
{
provider: 'dropbox',
desc: 'does watch files over time',
file: '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: '/b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
content: 'label from another window',
},
] as const;
Expand Down Expand Up @@ -44,7 +44,7 @@ describe('Metadata - suite is watching cloud provider and syncs periodically', (
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
aesKey: '998daf71f3fbc486076f0ee8d5737a61b82bceacb0ec69100cbe4d45cd79676a',
});
cy.prefixedVisit('/', {
onBeforeLoad: win => {
Expand Down Expand Up @@ -81,7 +81,7 @@ describe('Metadata - suite is watching cloud provider and syncs periodically', (
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
aesKey: '998daf71f3fbc486076f0ee8d5737a61b82bceacb0ec69100cbe4d45cd79676a',
});

// and this does the time travel to trigger fetch
Expand Down
94 changes: 94 additions & 0 deletions packages/suite-web/e2e/tests/metadata/metadata-migration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { rerouteMetadataToMockProvider, stubOpen } from '../../stubs/metadata';

const fileName = '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt';
const migratedFileName =
'/b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt';
const renamedFileName = '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca_v1.mtdt';

const fileLocation = {
google: fileName,
dropbox: fileName,
};

const provider = 'dropbox' as const;

const checkLabel = () => {
cy.getTestElement('@suite/menu/wallet-index').click();
cy.getTestElement('@account-menu/btc/normal/0/label').should('contain', 'some account label');
};

describe('Metadata - metadata files are properly migrated from ENCRYPTION_VERSION v1 to v2', () => {
beforeEach(() => {
cy.viewport(1080, 1440).resetDb();
cy.task('startEmu', { wipe: true });
cy.task('setupEmu', {
mnemonic: 'all all all all all all all all all all all all',
});
cy.task('startBridge');
cy.prefixedVisit('/', {
onBeforeLoad: (win: Window) => {
cy.stub(win, 'open').callsFake(stubOpen(win));
cy.stub(win, 'fetch').callsFake(rerouteMetadataToMockProvider);
},
});

cy.passThroughInitialRun();
cy.discoveryShouldFinish();

cy.getTestElement('@suite/menu/settings').click();
});

it(`should migrate metadata file from v1 to v2 using ${provider}`, () => {
// initialize provider
cy.task('metadataStartProvider', provider);
cy.task('metadataSetFileContent', {
provider,
file: fileLocation[provider],
content: {
version: '1.0.0',
accountLabel: 'some account label',
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
});

// enable labeling
cy.getTestElement('@settings/metadata-switch').click({ force: true });
cy.passThroughInitMetadata(provider);

// appears only when a migration is needed
cy.getConfirmActionOnDeviceModal();
cy.task('pressYes');

// wait until migration finishes
cy.getTestElement('@settings/metadata-switch').get('input').should('not.be.disabled');

// check if the file has been migrated
cy.task('metadataGetFilesList', { provider }).then((files: string[]) => {
const isFileMigrated = files.includes(migratedFileName);
const isFileRenamed = files.includes(renamedFileName);
expect(isFileMigrated).to.be.true;
expect(isFileRenamed).to.be.true;
expect(files.length).to.have.least(2); // dummies were created
});

// check if the label is there after migration
checkLabel();

// toggle labeling to see if the migration is not run again (no device prompt modal = no migration)
cy.getTestElement('@suite/menu/settings').click();
cy.getTestElement('@settings/metadata-switch').click({ force: false });
cy.getTestElement('@settings/metadata-switch').click({ force: true });

// wait until migration finishes
cy.getTestElement('@settings/metadata-switch').get('input').should('not.be.disabled');

// check if the label is still there
checkLabel();
});

// todo: migration failed, user reloads -> migration happens again
// todo: migration succeeded, user reloads -> no migration, yupi
// todo: check file was renamed, dummies were created
});
13 changes: 6 additions & 7 deletions packages/suite-web/e2e/tests/metadata/remembered-device.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { rerouteMetadataToMockProvider, stubOpen } from '../../stubs/metadata';
const providers = [
{
provider: 'google',
file: 'f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: 'b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
},
{
provider: 'dropbox',
file: '/f7acc942eeb83921892a95085e409b3e6b5325db6400ae5d8de523a305291dca.mtdt',
file: '/b9b5e1fd2800d4dc68e2f4e775fd819f4da3fb9e1bcc2cacd7f04fa543eac8a0_v2.mtdt',
},
] as const;

Expand Down Expand Up @@ -40,7 +40,7 @@ On disable, it throws away all metadata related records from memory.`, () => {
outputLabels: {},
addressLabels: {},
},
aesKey: 'c785ef250807166bffc141960c525df97647fcc1bca57f6892ca3742ba86ed8d',
aesKey: '998daf71f3fbc486076f0ee8d5737a61b82bceacb0ec69100cbe4d45cd79676a',
});

cy.prefixedVisit('/', {
Expand Down Expand Up @@ -105,11 +105,10 @@ On disable, it throws away all metadata related records from memory.`, () => {
cy.getTestElement('@account-menu/btc/normal/0/label').should('not.contain', 'label');
cy.hoverTestElement("@metadata/accountLabel/m/84'/0'/0'/hover-container");
cy.getTestElement("@metadata/accountLabel/m/84'/0'/0'/add-label-button").click();
cy.log(
'disabling metadata removed also all keys, so metadata init flow takes all steps now expect for providers, these stay connected',
cy.getTestElement('@account-menu/btc/normal/0/label').should(
'contain',
'already existing label',
);
cy.getConfirmActionOnDeviceModal();
cy.task('pressYes');

// device saved, disconnect provider
cy.getTestElement('@menu/switch-device').click();
Expand Down
Loading

0 comments on commit 19858e8

Please sign in to comment.