Skip to content

Commit

Permalink
Merge pull request #471 from translate-tools/468-60-version-not-work-…
Browse files Browse the repository at this point in the history
…on-chrome-1260647862

6.0 version not work on chrome
  • Loading branch information
vitonsky authored Jun 19, 2024
2 parents a8422c6 + 069e6bf commit 5501c85
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 18 deletions.
30 changes: 25 additions & 5 deletions src/app/ConfigStorage/ConfigStorage.migrations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import browser from 'webextension-polyfill';

import { DEFAULT_TRANSLATOR, DEFAULT_TTS } from '../../config';
import { DEFAULT_TRANSLATOR, DEFAULT_TTS, defaultConfig } from '../../config';
import { createMigrationTask, Migration } from '../../lib/migrations/createMigrationTask';
import { decodeStruct } from '../../lib/types';
import { AppConfig } from '../../types/runtime';

const migrations: Migration[] = [
{
Expand Down Expand Up @@ -29,7 +31,7 @@ const migrations: Migration[] = [
const mergedData = { ...actualData, ...storageData };

// Write data
browser.storage.local.set({
await browser.storage.local.set({
[storageNameV2]: mergedData,
});
}
Expand Down Expand Up @@ -63,7 +65,7 @@ const migrations: Migration[] = [
}

// Write data
browser.storage.local.set({
await browser.storage.local.set({
[storageNameV2]: {
...newData,
selectTranslator: {
Expand Down Expand Up @@ -104,7 +106,7 @@ const migrations: Migration[] = [
}

// Write data
browser.storage.local.set({ [storageName]: updatedConfig });
await browser.storage.local.set({ [storageName]: updatedConfig });
},
},
{
Expand All @@ -131,6 +133,24 @@ const migrations: Migration[] = [
await browser.storage.local.set({ [storageName]: updatedConfig });
},
},
{
version: 7,
async migrate() {
// Empty migration, to bump migration number and to trigger hook for repair config
},
},
];

export const ConfigStorageMigration = createMigrationTask(migrations);
export const ConfigStorageMigration = createMigrationTask(migrations, {
onComplete: async () => {
// Repair config if necessary
const storageName = 'appConfig';
const { [storageName]: config } = await browser.storage.local.get(storageName);

const { errors } = decodeStruct(AppConfig, config);
if (errors === null) return;

console.warn('Config object is invalid, fallback to default config', errors);
await browser.storage.local.set({ [storageName]: defaultConfig });
},
});
40 changes: 34 additions & 6 deletions src/app/ConfigStorage/__test__/ConfigStorage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { ConfigStorage, ObservableAsyncStorage } from '../ConfigStorage';
import { ConfigStorageMigration } from '../ConfigStorage.migrations';
import configVersion1 from './config-v1.json';
import configVersion3 from './config-v3.json';
import configVersion5 from './config-v5.json';
import configVersion6 from './config-v6.json';

const latestVersion = 6;
const latestConfigSample = configVersion6;

describe('config migrations', () => {
beforeAll(clearAllMocks);

test('migrate config v0-v3', async () => {
test('migrate config from v0 to v3', async () => {
// Load data
localStorage.setItem('config.Main', JSON.stringify(configVersion1));

Expand All @@ -26,7 +28,7 @@ describe('config migrations', () => {
expect(localStorage.getItem('config.Main')).toBeNull();
});

test('migrate config v0-v5', async () => {
test('migrate config v0 to latest version', async () => {
// Load data
localStorage.setItem(
'config.Main',
Expand All @@ -37,17 +39,43 @@ describe('config migrations', () => {
);

// Migrate data
await ConfigStorageMigration.migrate(0, 5);
await ConfigStorageMigration.migrate(0, latestVersion);

const { appConfig } = await browser.storage.local.get('appConfig');
expect(appConfig).toEqual(configVersion5);
expect(appConfig).toEqual(latestConfigSample);
});

describe(`race condition detection`, () => {
beforeEach(clearAllMocks);

for (let attempt = 1; attempt <= 5; attempt++) {
test(`Detection race conditions. Attempt #${attempt}`, async () => {
// Load data
localStorage.setItem(
'config.Main',
JSON.stringify({
...configVersion1,
translatorModule: 'BingTranslatorPublic',
}),
);

// Migrate part of data
await ConfigStorageMigration.migrate(0, 5);

// Migrate another part of data
await ConfigStorageMigration.migrate(5, latestVersion);

const { appConfig } = await browser.storage.local.get('appConfig');
expect(appConfig).toEqual(latestConfigSample);
});
}
});
});

describe('use config', () => {
beforeAll(clearAllMocks);

const latestConfigObject = configVersion6 as AppConfigType;
const latestConfigObject = latestConfigSample as AppConfigType;

test('config storage set/get', async () => {
const configStorage = new ConfigStorage(latestConfigObject);
Expand Down
8 changes: 2 additions & 6 deletions src/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,14 @@ export class App {
// Setup sandboxed iframes
if (isChromium()) {
// Currently `offscreen` API is non standard, so we cast type
const offscreen = (browser as any).offscreen;
const offscreen = (globalThis as any).chrome.offscreen;

// We may have only one offscreen document, but we need more,
// so we create only one "main" document, that creates embedded iframes
try {
offscreen.createDocument({
url: 'offscreen-documents/main/main.html',
reasons: [
offscreen.Reason.WORKERS,
offscreen.Reason.IFRAME_SCRIPTING,
offscreen.Reason.MATCH_MEDIA,
],
reasons: ['WORKERS', 'IFRAME_SCRIPTING', 'MATCH_MEDIA'],
justification:
'Main offscreen document, to run WASM and custom translators code in sandbox',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ export class PersistentMigrationsExecutor {

await migration.migrate(currentVersion, latestVersion);

const { hooks = {} } = migration;
if (hooks.onComplete) {
await hooks.onComplete();
}

// Update storage version
migrationsVersions[name] = latestVersion;
await this.storage.setMigrationsVersions(migrationsVersions);
Expand Down
12 changes: 11 additions & 1 deletion src/lib/migrations/createMigrationTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export type Migration = {
migrate: (previousVersion: number) => Promise<void>;
};

export type MigrationHooks = {
onComplete?: () => Promise<void>;
};

/**
* Task to migrate from one version to another
* Task may execute several migrations, so it may take a time
Expand All @@ -29,12 +33,17 @@ export type MigrationTask = {
* WARNING: previous version may contain `0` in case when data structure run migration first time
*/
migrate: (previousVersion: number, currentVersion: number) => Promise<void>;

hooks?: MigrationHooks;
};

/**
* Build migration task from migrations list
*/
export const createMigrationTask = (migrations: Migration[]): MigrationTask => {
export const createMigrationTask = (
migrations: Migration[],
hooks?: MigrationHooks,
): MigrationTask => {
const sortedMigrations = migrations.sort(
(migration1, migration2) => migration1.version - migration2.version,
);
Expand All @@ -47,6 +56,7 @@ export const createMigrationTask = (migrations: Migration[]): MigrationTask => {

return {
version: lastMigrationVersion,
hooks,
migrate: async (fromVersion: number, toVersion: number) => {
const migrationsToApply = sortedMigrations.filter(
(migration) =>
Expand Down
30 changes: 30 additions & 0 deletions test/setupFiles/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const addRandomDelaysForMethods = (object, methods) => {
function getRandomInt(max = 1) {
return Math.floor(Math.random() * max);
}

return new Proxy(object, {
get(target, prop) {
const object = target[prop];
if (typeof object !== 'function') return object;

const isNeedDelay = methods.includes(prop);

return (...args) => {
if (!isNeedDelay) {
return object(...args);
}

return Promise.resolve().then(async () => {
const delay = getRandomInt(20);

await new Promise((res) => setTimeout(res, delay));

return object(...args);
});
};
},
});
};

module.exports = { addRandomDelaysForMethods };
6 changes: 6 additions & 0 deletions test/setupFiles/webextension.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ globalThis.location = new URL('/_generated_background_page.html', extBasePath);
globalThis.navigator = {
userAgent: 'node.js',
};

const { addRandomDelaysForMethods } = require('./utils');

// Add random delays for async operations,
// to make tests closer to reality
chrome.storage.local = addRandomDelaysForMethods(chrome.storage.local, ['get', 'set']);

0 comments on commit 5501c85

Please sign in to comment.