blob: eeb0a0d0c266afc5a9d4e434d3f48cff10a5dac6 [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 type * as Common from '../../core/common/common.js';
import * as Platform from '../../core/platform/platform.js';
import * as ProtocolClient from '../../core/protocol_client/protocol_client.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as Protocol from '../../generated/protocol.js';
import {setupLocaleHooks} from '../../testing/LocaleHelpers.js';
import {MockCDPConnection} from '../../testing/MockCDPConnection.js';
import {setupRuntimeHooks} from '../../testing/RuntimeHelpers.js';
import {setupSettingsHooks} from '../../testing/SettingsHelpers.js';
import {TestUniverse} from '../../testing/TestUniverse.js';
import {createFileSystemUISourceCode} from '../../testing/UISourceCodeHelpers.js';
import * as Persistence from '../persistence/persistence.js';
import * as Workspace from '../workspace/workspace.js';
import * as Bindings from './bindings.js';
const {urlString} = Platform.DevToolsPath;
describe('StylesSourceMapping', () => {
setupLocaleHooks();
setupSettingsHooks();
setupRuntimeHooks();
let universe: TestUniverse;
beforeEach(() => {
universe = new TestUniverse();
});
it('does not overwrite CSS files when CSS model reports error on getStyleSheetText', async () => {
const connection = new MockCDPConnection();
// Stub DOM.enable and CSS.enable to succeed
connection.setSuccessHandler('DOM.enable', () => ({}));
connection.setSuccessHandler('CSS.enable', () => ({}));
// Stub CSS.setStyleSheetText to succeed
connection.setSuccessHandler('CSS.setStyleSheetText', () => ({sourceMapURL: ''}));
// Stub CSS.getStyleSheetText to fail
connection.setFailureHandler('CSS.getStyleSheetText',
() => ({
message: 'FAKE PROTOCOL ERROR',
code: ProtocolClient.CDPConnection.CDPErrorStatus.DEVTOOLS_STUB_ERROR,
}));
const target = universe.createTarget({connection});
const cssModel = target.model(SDK.CSSModel.CSSModel);
assert.exists(cssModel);
// Eagerly instantiate bindings and persistence
void universe.cssWorkspaceBinding;
const persistence = universe.persistence;
const styleSheetId = 'stylesheet' as Protocol.DOM.StyleSheetId;
const frameId = 'frame' as Protocol.Page.FrameId;
const sourceURL = urlString`http://example.com/simple.css`;
const headerPayload: Protocol.CSS.CSSStyleSheetHeader = {
styleSheetId,
frameId,
sourceURL,
origin: Protocol.CSS.StyleSheetOrigin.Regular,
title: 'simple.css',
disabled: false,
isInline: false,
isMutable: false,
isConstructed: false,
loadingFailed: false,
startLine: 0,
startColumn: 0,
length: 0,
endLine: 0,
endColumn: 0,
};
// Wait for the StylesSourceMapping to create the network UISourceCode
const networkUISourceCodePromise = new Promise<Workspace.UISourceCode.UISourceCode>(resolve => {
const listener = (event: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.UISourceCode>) => {
if (event.data.project().type() === Workspace.Workspace.projectTypes.Network &&
event.data.url() === sourceURL) {
universe.workspace.removeEventListener(Workspace.Workspace.Events.UISourceCodeAdded, listener);
resolve(event.data);
}
};
universe.workspace.addEventListener(Workspace.Workspace.Events.UISourceCodeAdded, listener);
});
cssModel.styleSheetAdded(headerPayload);
const networkUISourceCode = await networkUISourceCodePromise;
// Create filesystem UISourceCode
const fileSystemPath = urlString`file://path/to/filesystem`;
const fileSystemFileUrl = urlString`${fileSystemPath + '/simple.css'}`;
const origContent = 'body {\n color: red;\n}\n';
const {uiSourceCode: fileSystemUiSourceCode, project} = createFileSystemUISourceCode({
url: fileSystemFileUrl,
mimeType: 'text/css',
content: origContent,
fileSystemPath,
autoMapping: true,
type: Persistence.PlatformFileSystem.PlatformFileSystemType.WORKSPACE_PROJECT,
universe,
});
// Verify binding is created.
// Manually bind them.
const binding = new Persistence.Persistence.PersistenceBinding(networkUISourceCode, fileSystemUiSourceCode);
await persistence.addBindingForTest(binding);
// Verify initial content of filesystem
await fileSystemUiSourceCode.requestContentData();
assert.strictEqual(fileSystemUiSourceCode.workingCopy(), origContent);
const styleFileProto = Bindings.StylesSourceMapping.StyleFile.prototype as unknown as {
styleFileSyncedForTest: () => void,
};
const syncStub = sinon.stub(styleFileProto, 'styleFileSyncedForTest');
const syncPromise = new Promise<void>(resolve => {
syncStub.callsFake(() => {
resolve();
});
});
// Call setStyleSheetText on CSSModel
const setStyleSheetTextPromise = cssModel.setStyleSheetText(styleSheetId, 'body {color: blue}', true);
// Wait for sync (which should fail and return early)
await syncPromise;
await setStyleSheetTextPromise;
// Verify filesystem content is NOT changed
assert.strictEqual(fileSystemUiSourceCode.workingCopy(), origContent);
// Restore stubs
syncStub.restore();
project.dispose();
});
});