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:
Changhao Han
2020-03-26 00:48:03 +00:00
committed by Commit Bot
parent 3ca8bc5a4a
commit d5a3755f19
6 changed files with 117 additions and 1 deletions
@@ -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) {
+1
View File
@@ -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');
}
});
});
+16
View File
@@ -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>
+1
View File
@@ -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',