blob: 52dd43475c1ef47d90896c0cd0a3cc0930512c23 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import sinon from 'sinon';
import * as Root from '../root/root.js';
import * as Common from './common.js';
const InMemoryStorage = Common.Settings.InMemoryStorage;
describe('VersionController', () => {
let settings: Common.Settings.Settings;
let syncedStorage: Common.Settings.SettingsStorage;
let globalStorage: Common.Settings.SettingsStorage;
let localStorage: Common.Settings.SettingsStorage;
beforeEach(() => {
const mockStore = new InMemoryStorage();
syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
localStorage = new Common.Settings.SettingsStorage({}, mockStore);
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
});
describe('updateVersion', () => {
it('initializes version settings with the current version if the setting doesn\'t exist yet', () => {
assert.isFalse(globalStorage.has(Common.VersionController.VersionController.GLOBAL_VERSION_SETTING_NAME));
assert.isFalse(syncedStorage.has(Common.VersionController.VersionController.SYNCED_VERSION_SETTING_NAME));
assert.isFalse(localStorage.has(Common.VersionController.VersionController.LOCAL_VERSION_SETTING_NAME));
new Common.VersionController.VersionController(settings).updateVersion();
const currentVersion = Common.VersionController.VersionController.CURRENT_VERSION.toString();
assert.strictEqual(
globalStorage.get(Common.VersionController.VersionController.GLOBAL_VERSION_SETTING_NAME), currentVersion);
assert.strictEqual(
syncedStorage.get(Common.VersionController.VersionController.SYNCED_VERSION_SETTING_NAME), currentVersion);
assert.strictEqual(
localStorage.get(Common.VersionController.VersionController.LOCAL_VERSION_SETTING_NAME), currentVersion);
});
function spyAllUpdateMethods(versionController: Common.VersionController.VersionController) {
const spies: Array<sinon.SinonSpy<unknown[], unknown>> = [];
for (let i = 0; i < Common.VersionController.VersionController.CURRENT_VERSION; ++i) {
spies.push(sinon.spy(
versionController, `updateVersionFrom${i}To${i + 1}` as keyof Common.VersionController.VersionController));
}
return spies;
}
it('does not run any update* methods if no version setting exist yet', () => {
const versionController = new Common.VersionController.VersionController(settings);
const spies = spyAllUpdateMethods(versionController);
versionController.updateVersion();
for (const spy of spies) {
sinon.assert.notCalled(spy);
}
});
it('does not run any update* methods if all version settings are already current', () => {
const currentVersion = Common.VersionController.VersionController.CURRENT_VERSION.toString();
globalStorage.set(Common.VersionController.VersionController.GLOBAL_VERSION_SETTING_NAME, currentVersion);
syncedStorage.set(Common.VersionController.VersionController.SYNCED_VERSION_SETTING_NAME, currentVersion);
localStorage.set(Common.VersionController.VersionController.LOCAL_VERSION_SETTING_NAME, currentVersion);
const versionController = new Common.VersionController.VersionController(settings);
const spies = spyAllUpdateMethods(versionController);
versionController.updateVersion();
for (const spy of spies) {
sinon.assert.notCalled(spy);
}
});
it('runs correct update* methods if the local bucket lags behind', () => {
const currentVersion = Common.VersionController.VersionController.CURRENT_VERSION.toString();
const localVersion = (Common.VersionController.VersionController.CURRENT_VERSION - 3).toString();
globalStorage.set(Common.VersionController.VersionController.GLOBAL_VERSION_SETTING_NAME, currentVersion);
syncedStorage.set(Common.VersionController.VersionController.SYNCED_VERSION_SETTING_NAME, currentVersion);
localStorage.set(Common.VersionController.VersionController.LOCAL_VERSION_SETTING_NAME, localVersion);
const versionController = new Common.VersionController.VersionController(settings);
const spies = spyAllUpdateMethods(versionController);
versionController.updateVersion();
const expectedUncalledSpies = spies.slice(0, -3);
for (const spy of expectedUncalledSpies) {
sinon.assert.notCalled(spy);
}
const expectedCalledSpies = spies.slice(-3);
for (const spy of expectedCalledSpies) {
sinon.assert.called(spy);
}
});
it('runs correct update* methods if the synced bucket runs ahead', () => {
const currentVersion = Common.VersionController.VersionController.CURRENT_VERSION.toString();
const oldVersion = (Common.VersionController.VersionController.CURRENT_VERSION - 1).toString();
globalStorage.set(Common.VersionController.VersionController.GLOBAL_VERSION_SETTING_NAME, oldVersion);
syncedStorage.set(Common.VersionController.VersionController.SYNCED_VERSION_SETTING_NAME, currentVersion);
localStorage.set(Common.VersionController.VersionController.LOCAL_VERSION_SETTING_NAME, oldVersion);
const versionController = new Common.VersionController.VersionController(settings);
const spies = spyAllUpdateMethods(versionController);
versionController.updateVersion();
const expectedUncalledSpies = spies.slice(0, -1);
for (const spy of expectedUncalledSpies) {
sinon.assert.notCalled(spy);
}
const expectedCalledSpies = spies.slice(-1);
for (const spy of expectedCalledSpies) {
sinon.assert.called(spy);
}
});
});
describe('updateVersionFrom31To32', () => {
it('correctly adds resourceTypeName to breakpoints', () => {
const versionController = new Common.VersionController.VersionController(settings);
const breakpointsSetting = settings.createLocalSetting('breakpoints', [
{url: 'webpack:///src/foo.ts', lineNumber: 4, condition: '', enabled: false},
{url: 'foo.js', lineNumber: 1, columnNumber: 42, condition: 'false', enabled: true},
]);
versionController.updateVersionFrom31To32();
const breakpoints = breakpointsSetting.get();
assert.lengthOf(breakpoints, 2);
assert.propertyVal(breakpoints[0], 'url', 'webpack:///src/foo.ts');
assert.propertyVal(breakpoints[0], 'resourceTypeName', 'script');
assert.propertyVal(breakpoints[0], 'lineNumber', 4);
assert.notProperty(breakpoints[0], 'columnNumber');
assert.propertyVal(breakpoints[0], 'condition', '');
assert.propertyVal(breakpoints[0], 'enabled', false);
assert.propertyVal(breakpoints[1], 'url', 'foo.js');
assert.propertyVal(breakpoints[1], 'resourceTypeName', 'script');
assert.propertyVal(breakpoints[1], 'lineNumber', 1);
assert.propertyVal(breakpoints[1], 'columnNumber', 42);
assert.propertyVal(breakpoints[1], 'condition', 'false');
assert.propertyVal(breakpoints[1], 'enabled', true);
});
});
describe('updateVersionFrom32To33', () => {
it('correctly discards previously viewed files without url properties', () => {
const versionController = new Common.VersionController.VersionController(settings);
const previouslyViewedFilesSetting = settings.createLocalSetting('previouslyViewedFiles', [
{url: 'http://localhost:3000', scrollLineNumber: 1},
{scrollLineNumber: 1},
{},
{url: 'webpack:///src/foo.ts'},
]);
versionController.updateVersionFrom32To33();
const previouslyViewedFiles = previouslyViewedFilesSetting.get();
assert.lengthOf(previouslyViewedFiles, 2);
assert.propertyVal(previouslyViewedFiles[0], 'url', 'http://localhost:3000');
assert.notProperty(previouslyViewedFiles[0], 'selectionRange');
assert.propertyVal(previouslyViewedFiles[0], 'scrollLineNumber', 1);
assert.propertyVal(previouslyViewedFiles[1], 'url', 'webpack:///src/foo.ts');
assert.notProperty(previouslyViewedFiles[1], 'selectionRange');
assert.notProperty(previouslyViewedFiles[1], 'scrollLineNumber');
});
it('correctly adds resourceTypeName to previously viewed files', () => {
const versionController = new Common.VersionController.VersionController(settings);
const previouslyViewedFilesSetting = settings.createLocalSetting('previouslyViewedFiles', [
{url: 'http://localhost:3000', scrollLineNumber: 1},
{url: 'webpack:///src/foo.ts'},
]);
versionController.updateVersionFrom32To33();
const previouslyViewedFiles = previouslyViewedFilesSetting.get();
assert.lengthOf(previouslyViewedFiles, 2);
assert.propertyVal(previouslyViewedFiles[0], 'url', 'http://localhost:3000');
assert.propertyVal(previouslyViewedFiles[0], 'resourceTypeName', 'script');
assert.notProperty(previouslyViewedFiles[0], 'selectionRange');
assert.propertyVal(previouslyViewedFiles[0], 'scrollLineNumber', 1);
assert.propertyVal(previouslyViewedFiles[1], 'url', 'webpack:///src/foo.ts');
assert.propertyVal(previouslyViewedFiles[1], 'resourceTypeName', 'script');
assert.notProperty(previouslyViewedFiles[1], 'selectionRange');
assert.notProperty(previouslyViewedFiles[1], 'scrollLineNumber');
});
});
describe('updateVersionFrom33To34', () => {
it('correctly adds isLogpoint to breakpoints', () => {
const versionController = new Common.VersionController.VersionController(settings);
const breakpointsSetting = settings.createLocalSetting('breakpoints', [
{
url: 'webpack:///src/foo.ts',
lineNumber: 4,
resourceTypeName: 'script',
condition: '/** DEVTOOLS_LOGPOINT */ console.log(foo.property)',
enabled: true,
},
{
url: 'foo.js',
lineNumber: 1,
columnNumber: 42,
resourceTypeName: 'script',
condition: 'x === 42',
enabled: true,
},
{url: 'bar.js', lineNumber: 5, columnNumber: 1, resourceTypeName: 'script', condition: '', enabled: true},
]);
versionController.updateVersionFrom33To34();
const breakpoints = breakpointsSetting.get();
assert.propertyVal(breakpoints[0], 'isLogpoint', true);
assert.propertyVal(breakpoints[1], 'isLogpoint', false);
assert.propertyVal(breakpoints[2], 'isLogpoint', false);
});
});
describe('updateVersionFrom34To35', () => {
it('removes the logpoint prefix/suffix from logpoints', () => {
const versionController = new Common.VersionController.VersionController(settings);
const breakpointsSetting =
settings.createLocalSetting('breakpoints', [{
url: 'webpack:///src/foo.ts',
lineNumber: 4,
resourceTypeName: 'script',
condition: '/** DEVTOOLS_LOGPOINT */ console.log(foo.property)',
enabled: true,
isLogpoint: true,
}]);
versionController.updateVersionFrom34To35();
const breakpoints = breakpointsSetting.get();
assert.lengthOf(breakpoints, 1);
assert.propertyVal(breakpoints[0], 'condition', 'foo.property');
});
it('leaves conditional breakpoints alone', () => {
const versionController = new Common.VersionController.VersionController(settings);
const breakpointsSetting = settings.createLocalSetting('breakpoints', [{
url: 'webpack:///src/foo.ts',
lineNumber: 4,
resourceTypeName: 'script',
condition: 'x === 42',
enabled: true,
isLogpoint: false,
}]);
versionController.updateVersionFrom34To35();
const breakpoints = breakpointsSetting.get();
assert.lengthOf(breakpoints, 1);
assert.propertyVal(breakpoints[0], 'condition', 'x === 42');
});
});
describe('updateVersionFrom35To36', () => {
it('update showThirdPartyIssues setting value to true', () => {
const versionController = new Common.VersionController.VersionController(settings);
const showThirdPartyIssuesSetting = settings.createLocalSetting('showThirdPartyIssues', false);
versionController.updateVersionFrom35To36();
assert.isTrue(showThirdPartyIssuesSetting.get());
});
});
describe('updateVersionFrom36To37', () => {
it('updates all keys to kebab case', () => {
const versionController = new Common.VersionController.VersionController(settings);
settings.globalStorage.set('globalSetting1', '');
settings.globalStorage.set('globalSetting2', '');
settings.localStorage.set('localSetting', '');
settings.syncedStorage.set('syncedSetting', '');
versionController.updateVersionFrom36To37();
assert.deepEqual(settings.globalStorage.keys(), ['global-setting-1', 'global-setting-2']);
assert.deepEqual(settings.localStorage.keys(), ['local-setting']);
assert.deepEqual(settings.syncedStorage.keys(), ['synced-setting']);
});
it('keeps kebab case settings as is', () => {
const versionController = new Common.VersionController.VersionController(settings);
settings.globalStorage.set('setting', '123');
settings.localStorage.set('another-setting', '456');
versionController.updateVersionFrom36To37();
assert.deepEqual(settings.globalStorage.keys(), ['setting']);
assert.strictEqual(settings.globalStorage.get('setting'), '123');
assert.strictEqual(settings.localStorage.get('another-setting'), '456');
});
it('update data grid column weights value', () => {
const versionController = new Common.VersionController.VersionController(settings);
settings.globalStorage.set('dataGrid-foo-columnWeights', JSON.stringify({
columnOne: 1,
columnTwo: 2,
}));
versionController.updateVersionFrom36To37();
const setting = settings.createSetting('data-grid-foo-column-weights', {});
assert.deepEqual(setting.get(), {'column-one': 1, 'column-two': 2});
});
it('update view manager settings values', () => {
const versionController = new Common.VersionController.VersionController(settings);
settings.globalStorage.set('viewsLocationOverride', JSON.stringify({
somePanel: 'main',
other_panel: 'drawer',
}));
settings.globalStorage.set('closeableTabs', JSON.stringify({
somePanel: false,
other_panel: true,
}));
settings.globalStorage.set('main-tabOrder', JSON.stringify({
somePanel: 2,
other_panel: 1,
}));
settings.globalStorage.set('main-selectedTab', JSON.stringify('somePanel'));
versionController.updateVersionFrom36To37();
assert.deepEqual(
settings.createSetting('views-location-override', {}).get(), {'some-panel': 'main', 'other-panel': 'drawer'});
assert.deepEqual(settings.createSetting('closeable-tabs', {}).get(), {'some-panel': false, 'other-panel': true});
assert.deepEqual(settings.createSetting('main-tab-order', {}).get(), {'some-panel': 2, 'other-panel': 1});
assert.deepEqual(settings.createSetting('main-selected-tab', '').get(), 'some-panel');
});
});
});
describe('updateVersionFrom37To38', () => {
let settings: Common.Settings.Settings;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
Common.Settings.registerSettingExtension({
settingName: 'console-insights-enabled',
settingType: Common.Settings.SettingType.BOOLEAN,
defaultValue: true,
});
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
});
afterEach(() => {
Common.Settings.resetSettings(); // Clear SettingsRegistrations.
});
it('disables console insights setting if onboarding not done', () => {
const versionController = new Common.VersionController.VersionController(settings);
const consoleInsightsEnabled: Common.Settings.Setting<boolean> = settings.moduleSetting('console-insights-enabled');
consoleInsightsEnabled.set(true);
const onboardingFinished = settings.createLocalSetting('console-insights-onboarding-finished', false);
versionController.updateVersionFrom37To38();
assert.isFalse(consoleInsightsEnabled.get());
assert.isFalse(onboardingFinished.get());
});
it('preserves state if console insights disabled and not onboarded ', () => {
const versionController = new Common.VersionController.VersionController(settings);
const consoleInsightsEnabled: Common.Settings.Setting<boolean> = settings.moduleSetting('console-insights-enabled');
consoleInsightsEnabled.set(false);
const onboardingFinished = settings.createLocalSetting('console-insights-onboarding-finished', false);
versionController.updateVersionFrom37To38();
assert.isFalse(consoleInsightsEnabled.get());
assert.isFalse(onboardingFinished.get());
});
it('preserves state if console insights enabled and onboarded', () => {
const versionController = new Common.VersionController.VersionController(settings);
const consoleInsightsEnabled: Common.Settings.Setting<boolean> = settings.moduleSetting('console-insights-enabled');
consoleInsightsEnabled.set(true);
const onboardingFinished = settings.createLocalSetting('console-insights-onboarding-finished', true);
versionController.updateVersionFrom37To38();
assert.isTrue(consoleInsightsEnabled.get());
assert.isTrue(onboardingFinished.get());
});
it('resets onboarding if console insights setting is disabled', () => {
const versionController = new Common.VersionController.VersionController(settings);
const consoleInsightsEnabled: Common.Settings.Setting<boolean> = settings.moduleSetting('console-insights-enabled');
consoleInsightsEnabled.set(false);
const onboardingFinished = settings.createLocalSetting('console-insights-onboarding-finished', true);
versionController.updateVersionFrom37To38();
assert.isFalse(consoleInsightsEnabled.get());
assert.isFalse(onboardingFinished.get());
});
});
describe('updateVersionFrom38To39', () => {
let settings: Common.Settings.Settings;
let setting: Common.Settings.Setting<{title: string, i18nTitleKey: string}>;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
setting = settings.createSetting('preferred-network-condition', {title: 'Offline', i18nTitleKey: 'Offline'});
});
afterEach(() => {
Common.Settings.resetSettings(); // Clear SettingsRegistrations.
});
it('renames the preferred-network-condition for "Slow 3G"', async () => {
setting.set({title: 'Slow 3G', i18nTitleKey: 'Slow 3G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom38To39();
const newSetting = await setting.forceGet();
assert.strictEqual(newSetting.title, '3G');
assert.strictEqual(newSetting.i18nTitleKey, '3G');
});
it('renames the preferred-network-condition for "Fast 3G"', async () => {
setting.set({title: 'Fast 3G', i18nTitleKey: 'Fast 3G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom38To39();
const newSetting = await setting.forceGet();
assert.strictEqual(newSetting.title, 'Slow 4G');
assert.strictEqual(newSetting.i18nTitleKey, 'Slow 4G');
});
it('does not rename any other setting', async () => {
setting.set({title: 'Slow 4G', i18nTitleKey: 'Slow 4G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom38To39();
const newSetting = await setting.forceGet();
assert.strictEqual(newSetting.title, 'Slow 4G');
assert.strictEqual(newSetting.i18nTitleKey, 'Slow 4G');
});
it('deletes the setting if it does not parse as valid JSON', async () => {
setting.set({title: 'Slow 4G', i18nTitleKey: 'Slow 4G'});
sinon.stub(JSON, 'parse').callsFake(() => {
throw new Error('Invalid JSON');
});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom38To39();
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
describe('updateVersionFrom39To40', () => {
let settings: Common.Settings.Settings;
let customNetworkCondSetting: Common.Settings.Setting<Array<{key?: string}>>;
let preferredNetworkCondSetting: Common.Settings.Setting<{i18nTitleKey: string}>;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
Common.Settings.registerSettingExtension({
settingName: 'custom-network-conditions',
settingType: Common.Settings.SettingType.ARRAY,
defaultValue: [],
});
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
customNetworkCondSetting = settings.moduleSetting('custom-network-conditions');
preferredNetworkCondSetting = settings.createSetting('preferred-network-condition', {i18nTitleKey: 'Offline'});
});
afterEach(() => {
Common.Settings.resetSettings(); // Clear SettingsRegistrations.
});
it('updates all settings to have a key', () => {
// In reality these values are SDK.NetworkManager.Conditions but we
// cannot refer to SDK here, and for this test we are only testing the
// addition of the key which does not care about the data in the
// object.
customNetworkCondSetting.set([{}, {}]);
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
assert.deepEqual(customNetworkCondSetting.get(), [
{
key: 'USER_CUSTOM_SETTING_1',
},
{
key: 'USER_CUSTOM_SETTING_2',
},
]);
});
it('does not update settings that have a key already', () => {
customNetworkCondSetting.set([{key: 'KEY'}]);
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
assert.deepEqual(customNetworkCondSetting.get(), [
{
key: 'KEY',
},
]);
});
it('migrates users who have a preferred-network-condition set to "Fast 4G"', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'Fast 4G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('SPEED_FAST_4G'));
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('migrates users who have a preferred-network-condition set to "Slow 4G"', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'Slow 4G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('SPEED_SLOW_4G'));
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('migrates users who have a preferred-network-condition set to "3G"', () => {
preferredNetworkCondSetting.set({i18nTitleKey: '3G'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('SPEED_3G'));
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('migrates users who have a preferred-network-condition set to "Offline"', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'Offline'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('OFFLINE'));
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('sets the default setting value correctly to No Throttling', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'Offline'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('OFFLINE'));
const newSetting = settings.createSetting('active-network-condition-key', 'INVALID');
assert.strictEqual(newSetting.defaultValue, 'NO_THROTTLING');
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('migrates users who have a preferred-network-condition set to "No throttling"', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'No throttling'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
const activeKeySetting = settings.globalStorage.get('active-network-condition-key');
assert.strictEqual(activeKeySetting, JSON.stringify('NO_THROTTLING'));
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
it('ignores any unexpected values and just deletes the old setting', () => {
preferredNetworkCondSetting.set({i18nTitleKey: 'Not a valid key'});
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom39To40();
// Ensure it does not create the new setting, ensuring that it will be
// created when the user next navigates to the network / perf panel.
assert.isFalse(settings.globalStorage.has('active-network-condition-key'));
// We still get rid of the old value.
assert.isFalse(settings.globalStorage.has('preferred-network-condition'));
});
});
});
describe('updateVersionFrom40To41', () => {
let settings: Common.Settings.Settings;
let hideNetworkMessagesSetting: Common.Settings.Setting<boolean>;
let networkMessagesSetting: Common.Settings.Setting<boolean>;
let hideChromeFrameSetting: Common.Settings.Setting<boolean>;
let chromeFrameSetting: Common.Settings.Setting<boolean>;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
hideNetworkMessagesSetting =
settings.createSetting('hide-network-messages', false, Common.Settings.SettingStorageType.SYNCED);
networkMessagesSetting =
settings.createSetting('network-messages', true, Common.Settings.SettingStorageType.SYNCED);
hideChromeFrameSetting =
settings.createSetting('frame-viewer-hide-chrome-window', false, Common.Settings.SettingStorageType.SYNCED);
chromeFrameSetting =
settings.createSetting('frame-viewer-chrome-window', true, Common.Settings.SettingStorageType.SYNCED);
});
it('migrates network messages setting', () => {
hideNetworkMessagesSetting.set(true); // User had "Hide network messages" changed from default value to ON
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom40To41();
assert.isFalse(networkMessagesSetting.get()); // Should now have "Network messages" OFF
assert.isFalse(settings.syncedStorage.has('hide-network-messages'));
});
it('migrates chrome frame setting', () => {
hideChromeFrameSetting.set(true); // User had "Hide chrome frame" changed from default value to ON
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom40To41();
assert.isFalse(chromeFrameSetting.get()); // Should now have "Chrome frame" OFF
assert.isFalse(settings.syncedStorage.has('frame-viewer-hide-chrome-window'));
});
it('does not overwrite existing new settings', () => {
hideNetworkMessagesSetting.set(true);
hideChromeFrameSetting.set(true);
// User already started using the new setting
networkMessagesSetting.set(true);
chromeFrameSetting.set(true);
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom40To41();
assert.isTrue(networkMessagesSetting.get());
assert.isTrue(chromeFrameSetting.get());
// Should NOT have been flipped to false
assert.isFalse(settings.syncedStorage.has('hide-network-messages'));
assert.isFalse(settings.syncedStorage.has('frame-viewer-hide-chrome-window'));
});
});
describe('updateVersionFrom41To42', () => {
let settings: Common.Settings.Settings;
let recordingsSetting: Common.Settings.Setting<Array<{storageName: string, flow: {title: string, steps: unknown[]}}>>;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
recordingsSetting = settings.createSetting('recorder-recordings-ng', []);
});
it('work if setting is empty', () => {
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom41To42();
assert.deepEqual(recordingsSetting.get(), []);
});
it('trims title', () => {
recordingsSetting.set([
{storageName: '1', flow: {title: 'a'.repeat(350), steps: []}},
]); // User had "Hide chrome frame" changed from default value to ON
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom41To42();
const first = recordingsSetting.get()[0];
assert.isTrue(first.flow.title.length <= 300);
});
it('trims steps', async () => {
recordingsSetting.set([
{storageName: '1', flow: {title: 'a', steps: Array(5000).fill({})}},
]); // User had "Hide chrome frame" changed from default value to ON
const versionController = new Common.VersionController.VersionController(settings);
versionController.updateVersionFrom41To42();
const first = recordingsSetting.get()[0];
assert.isTrue(first.flow.steps.length <= 4096);
});
});
function describeExperimentMigration(
versionFrom: number,
versionTo: number,
settingName: string,
experimentName: Root.ExperimentNames.ExperimentName,
): void {
const updateMethodName =
`updateVersionFrom${versionFrom}To${versionTo}` as keyof Common.VersionController.VersionController;
describe(updateMethodName, () => {
let settings: Common.Settings.Settings;
let syncedStorage: Common.Settings.SettingsStorage;
let globalStorage: Common.Settings.SettingsStorage;
let localStorage: Common.Settings.SettingsStorage;
beforeEach((): void => {
const mockStore = new Common.Settings.InMemoryStorage();
syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
localStorage = new Common.Settings.SettingsStorage({}, mockStore);
Common.Settings.registerSettingExtension({
settingName,
settingType: Common.Settings.SettingType.BOOLEAN,
defaultValue: false,
storageType: Common.Settings.SettingStorageType.SYNCED,
});
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
runSettingsMigration: false,
console: new Common.Console.Console(),
});
});
afterEach((): void => {
Common.Settings.resetSettings();
});
it(`does nothing if ${experimentName} experiment is not enabled`, (): void => {
const versionController = new Common.VersionController.VersionController(settings);
const setting = settings.moduleSetting(settingName);
setting.set(false);
(versionController[updateMethodName] as () => void)();
assert.isFalse(setting.get());
});
it(`sets ${settingName} setting to true if experiment is enabled`, (): void => {
const versionController = new Common.VersionController.VersionController(settings);
const setting = settings.moduleSetting(settingName);
const getValueFromStorageStub = sinon.stub(Root.Runtime.experiments, 'getValueFromStorage');
getValueFromStorageStub.withArgs(experimentName).returns(true);
(versionController[updateMethodName] as () => void)();
assert.isTrue(setting.get());
getValueFromStorageStub.restore();
});
it(`does not overwrite ${settingName} setting if already present in syncedStorage`, (): void => {
const versionController = new Common.VersionController.VersionController(settings);
const setting = settings.moduleSetting(settingName);
setting.set(true);
const getValueFromStorageStub = sinon.stub(Root.Runtime.experiments, 'getValueFromStorage');
getValueFromStorageStub.withArgs(experimentName).returns(true);
const moduleSettingSpy = sinon.spy(settings, 'moduleSetting');
(versionController[updateMethodName] as () => void)();
sinon.assert.notCalled(moduleSettingSpy);
assert.isTrue(setting.get());
getValueFromStorageStub.restore();
moduleSettingSpy.restore();
});
it('does not crash if setting is not registered', (): void => {
const versionController = new Common.VersionController.VersionController(settings);
const getValueFromStorageStub = sinon.stub(Root.Runtime.experiments, 'getValueFromStorage');
getValueFromStorageStub.withArgs(experimentName).returns(true);
const moduleSettingStub = sinon.stub(settings, 'moduleSetting');
moduleSettingStub.withArgs(settingName).throws();
(versionController[updateMethodName] as () => void)();
moduleSettingStub.restore();
getValueFromStorageStub.restore();
});
});
}
describeExperimentMigration(
42, 43, 'timeline-show-all-events', 'timeline-show-all-events' as Root.ExperimentNames.ExperimentName);
describeExperimentMigration(43, 44, 'apca', 'apca' as Root.ExperimentNames.ExperimentName);
describeExperimentMigration(
44, 45, 'timeline-debug-mode', 'timeline-debug-mode' as Root.ExperimentNames.ExperimentName);
describeExperimentMigration(
45, 46, 'timeline-invalidation-tracking', 'timeline-invalidation-tracking' as Root.ExperimentNames.ExperimentName);
describe('access logging', () => {
let settings: Common.Settings.Settings;
let logSettingAccess!: sinon.SinonSpy;
beforeEach(() => {
const mockStore = new InMemoryStorage();
const syncedStorage = new Common.Settings.SettingsStorage({}, mockStore);
const globalStorage = new Common.Settings.SettingsStorage({}, mockStore);
const localStorage = new Common.Settings.SettingsStorage({}, mockStore);
logSettingAccess = sinon.spy();
settings = new Common.Settings.Settings({
syncedStorage,
globalStorage,
localStorage,
settingRegistrations: Common.SettingRegistration.getRegisteredSettings(),
logSettingAccess,
console: new Common.Console.Console(),
});
});
it('logs access on the first read', async () => {
const setting = settings.createSetting('test-setting', false);
sinon.assert.notCalled(logSettingAccess);
setting.get();
assert.isTrue(logSettingAccess.calledOnceWith('test-setting', false));
setting.get();
sinon.assert.calledOnce(logSettingAccess);
});
it('logs access on the every write', async () => {
const setting = settings.createSetting('test-setting', false);
setting.set(true);
assert.isTrue(logSettingAccess.calledOnceWith('test-setting', true));
setting.set(false);
sinon.assert.calledTwice(logSettingAccess);
assert.deepEqual(logSettingAccess.secondCall.args, ['test-setting', false]);
});
});