mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-29 16:57:00 +08:00
Adds check to ensure style pane update
Deleting the last property in a styles section incorrectly skips style pane update. This check ensures style pane update when the property's index is out-of-bound. Bug: chromium:1060267 Change-Id: Ie6f6a436c789745bcd1f5ece6162cc4ada82a5b3 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2107525 Commit-Queue: Changhao Han <changhaohan@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
This commit is contained in:
@@ -1151,8 +1151,12 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
||||
}
|
||||
this._parentPane.setUserOperation(false);
|
||||
|
||||
// TODO: using this.property.index to access its containing StyleDeclaration's property will result in
|
||||
// off-by-1 errors when the containing StyleDeclaration's respective property has already been deleted.
|
||||
// These referencing logic needs to be updated to be more robust.
|
||||
const updatedProperty = property || this._style.propertyAt(this.property.index);
|
||||
if (!success || !updatedProperty) {
|
||||
const isPropertyWithinBounds = this.property.index < this._style.allProperties().length;
|
||||
if (!success || (!updatedProperty && isPropertyWithinBounds)) {
|
||||
if (majorChange) {
|
||||
// It did not apply, cancel editing.
|
||||
if (this._newProperty) {
|
||||
|
||||
@@ -9,6 +9,7 @@ ts_library("elements") {
|
||||
sources = [
|
||||
"pseudo-states.ts",
|
||||
"shadowroot-styles.ts",
|
||||
"style-pane-properties.ts",
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
// 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 {describe, it} from 'mocha';
|
||||
import * as puppeteer from 'puppeteer';
|
||||
|
||||
import {$, click, getBrowserAndPages, resetPages, resourcesPath, waitFor} from '../../shared/helper.js';
|
||||
import {assertContentOfSelectedElementsNode, getAriaLabelSelectorFromPropertiesSelector, getDisplayedCSSPropertyNames, waitForElementsStyleSection} from '../helpers/elements-helpers.js';
|
||||
|
||||
const PROPERTIES_TO_DELETE_SELECTOR = '#properties-to-delete';
|
||||
const FIRST_PROPERTY_NAME_SELECTOR = '.tree-outline li:nth-of-type(1) > .webkit-css-property';
|
||||
const SECOND_PROPERTY_VALUE_SELECTOR = '.tree-outline li:nth-of-type(2) > .value';
|
||||
|
||||
const deletePropertyByBackspace = async (selector: string, root?: puppeteer.JSHandle<any>) => {
|
||||
const {frontend} = getBrowserAndPages();
|
||||
await click(selector, {root});
|
||||
await frontend.keyboard.press('Backspace');
|
||||
await frontend.keyboard.press('Tab');
|
||||
};
|
||||
|
||||
describe('The Elements Tab', async () => {
|
||||
beforeEach(async () => {
|
||||
await resetPages();
|
||||
});
|
||||
|
||||
it('can remove a CSS property when its name or value is deleted', async () => {
|
||||
const {target, frontend} = getBrowserAndPages();
|
||||
|
||||
await target.goto(`${resourcesPath}/elements/style-pane-properties.html`);
|
||||
await click('#tab-elements');
|
||||
|
||||
// Sanity check to make sure we have the correct node selected after opening a file
|
||||
await assertContentOfSelectedElementsNode('<body>\u200B');
|
||||
|
||||
// Select div that we will remove the CSS properties from
|
||||
await frontend.keyboard.press('ArrowRight');
|
||||
await assertContentOfSelectedElementsNode('<div id=\u200B"properties-to-delete">\u200B</div>\u200B');
|
||||
await waitForElementsStyleSection();
|
||||
{
|
||||
const displayedNames = await getDisplayedCSSPropertyNames(PROPERTIES_TO_DELETE_SELECTOR);
|
||||
assert.deepEqual(
|
||||
displayedNames,
|
||||
[
|
||||
'height',
|
||||
'width',
|
||||
],
|
||||
'incorrectly displayed style after initialization');
|
||||
}
|
||||
|
||||
const propertiesSection = await $(getAriaLabelSelectorFromPropertiesSelector(PROPERTIES_TO_DELETE_SELECTOR));
|
||||
// select second property's value and delete
|
||||
await deletePropertyByBackspace(SECOND_PROPERTY_VALUE_SELECTOR, propertiesSection);
|
||||
await waitForElementsStyleSection();
|
||||
await waitFor('.tree-outline .child-editing', propertiesSection);
|
||||
|
||||
// verify the second CSS property entry has been removed
|
||||
{
|
||||
const displayedNames = await getDisplayedCSSPropertyNames(PROPERTIES_TO_DELETE_SELECTOR);
|
||||
assert.deepEqual(
|
||||
displayedNames,
|
||||
[
|
||||
'height',
|
||||
],
|
||||
'incorrectly displayed style after removing second property\'s value');
|
||||
}
|
||||
|
||||
// select first property's name and delete
|
||||
await deletePropertyByBackspace(FIRST_PROPERTY_NAME_SELECTOR, propertiesSection);
|
||||
await waitForElementsStyleSection();
|
||||
await waitFor('.tree-outline .child-editing', propertiesSection);
|
||||
|
||||
// verify the first CSS property entry has been removed
|
||||
{
|
||||
await waitForElementsStyleSection();
|
||||
const displayedValues = await getDisplayedCSSPropertyNames(PROPERTIES_TO_DELETE_SELECTOR);
|
||||
assert.deepEqual(displayedValues, [], 'incorrectly displayed style after removing first property\'s name');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import {assert} from 'chai';
|
||||
import {$, $$, click, getBrowserAndPages, waitFor} from '../../shared/helper.js';
|
||||
|
||||
const SELECTED_TREE_ELEMENT_SELECTOR = '.selected[role="treeitem"]';
|
||||
const CSS_PROPERTY_NAME_SELECTOR = '.webkit-css-property';
|
||||
|
||||
export const assertContentOfSelectedElementsNode = async (expectedTextContent: string) => {
|
||||
const selectedNode = await $(SELECTED_TREE_ELEMENT_SELECTOR);
|
||||
@@ -78,3 +79,18 @@ export const getDisplayedEventListenerNames = async(): Promise<string[]> => {
|
||||
});
|
||||
return eventListenerNames;
|
||||
};
|
||||
|
||||
export const getAriaLabelSelectorFromPropertiesSelector = (selectorForProperties: string) =>
|
||||
`[aria-label="${selectorForProperties}, css selector"]`;
|
||||
|
||||
export const getDisplayedCSSPropertyNames = async (selectorForProperties: string) => {
|
||||
const listNodesContent = (nodes: Element[]) => {
|
||||
const rawContent = nodes.map(node => node.textContent);
|
||||
const filteredContent = rawContent.filter(content => !!content);
|
||||
return filteredContent;
|
||||
};
|
||||
const propertiesSection = await $(getAriaLabelSelectorFromPropertiesSelector(selectorForProperties));
|
||||
const cssPropertyNames = await $$(CSS_PROPERTY_NAME_SELECTOR, propertiesSection);
|
||||
const propertyNamesText = await cssPropertyNames.evaluate(listNodesContent);
|
||||
return propertyNamesText;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<!--
|
||||
Copyright 2020 The Chromium Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
#properties-to-delete {
|
||||
height: 10px;
|
||||
width: 20px;
|
||||
}
|
||||
</style>
|
||||
<div id="properties-to-delete"></div>
|
||||
@@ -12,6 +12,7 @@ const tests = [
|
||||
'elements/pseudo-states.js',
|
||||
'elements/shadowroot-styles.js',
|
||||
'elements/sidebar-event-listeners.js',
|
||||
'elements/style-pane-properties.js',
|
||||
'host/user-metrics.js',
|
||||
'network/network-datagrid.js',
|
||||
'rendering/vision-deficiencies.js',
|
||||
|
||||
Reference in New Issue
Block a user