Revert "test(devtools): revive cypress tests (#61972)" (#62243)

This reverts commit 75d246e03c.

PR Close #62243
This commit is contained in:
Jessica Janiuk
2025-06-24 17:00:43 +02:00
parent a42169a7ab
commit 9d20d6fbfc
18 changed files with 150 additions and 181 deletions
-9
View File
@@ -56,15 +56,6 @@ jobs:
run: yarn devtools:test
- name: Test build
run: yarn devtools:build:chrome
- name: Install Cypress
run: yarn global add cypress@14.4.1
- name: Cypress run
uses: cypress-io/github-action@6c143abc292aa835d827652c2ea025d098311070 # v6.10.1
with:
command: cypress run --project ./devtools/cypress
start: yarn bazel run //devtools/src:devserver
wait-on: 'http://localhost:4200'
wait-on-timeout: 300
test:
runs-on: ubuntu-latest-4core
-9
View File
@@ -60,15 +60,6 @@ jobs:
run: yarn devtools:test
- name: Test build
run: yarn devtools:build:chrome
- name: Install Cypress
run: yarn global add cypress@14.4.1
- name: Cypress run
uses: cypress-io/github-action@6c143abc292aa835d827652c2ea025d098311070 # v6.10.1
with:
command: cypress run --project ./devtools/cypress
start: yarn bazel run //devtools/src:devserver
wait-on: 'http://localhost:4200'
wait-on-timeout: 300
test:
runs-on: ubuntu-latest-8core
-15
View File
@@ -1,15 +0,0 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
module.exports = {
e2e: {
specPattern: 'integration/*.e2e.js',
supportFile: 'support/index.js',
baseUrl: 'http://localhost:4200',
},
};
+7 -5
View File
@@ -6,13 +6,15 @@
* found in the LICENSE file at https://angular.dev/license
*/
require('cypress-iframe');
describe('Testing the Todo app Demo', () => {
beforeEach(() => {
cy.visit('/');
});
it('should contain the todos application', () => {
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().contains('Todos');
getBody().contains('About');
getBody().contains('Clear completed');
@@ -21,15 +23,15 @@ describe('Testing the Todo app Demo', () => {
});
it('should contain the "Components" tab', () => {
cy.contains('.devtools-nav', 'Components');
cy.contains('.mat-tab-links', 'Components');
});
it('should contain the "Profiler" tab', () => {
cy.contains('.devtools-nav', 'Profiler');
cy.contains('.mat-tab-links', 'Profiler');
});
it('should contain "app-root" and "app-todo-demo" in the component tree', () => {
cy.contains('ng-tree-node', 'app-root');
cy.contains('ng-tree-node', 'app-todo-demo');
cy.contains('.tree-node', 'app-root');
cy.contains('.tree-node', 'app-todo-demo');
});
});
@@ -8,7 +8,7 @@
function showComments() {
cy.get('#nav-buttons > button:nth-child(2)').click();
cy.get('.cdk-overlay-container mat-slide-toggle label:contains("Show comment nodes")').click();
cy.get('#mat-slide-toggle-3 > label > div').click();
}
describe('Comment nodes', () => {
@@ -17,14 +17,14 @@ describe('Comment nodes', () => {
});
it('should not find any comment nodes by default', () => {
const nodes = cy.$$('ng-tree-node:contains("#comment")');
const nodes = cy.$$('.tree-node:contains("#comment")');
expect(nodes.length).to.eql(0);
});
it('should find comment nodes when the setting is enabled', () => {
showComments();
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("#comment")')
.find('.tree-node:contains("#comment")')
.its('length')
.should('not.eq', 0);
});
+1 -4
View File
@@ -12,9 +12,6 @@ describe('Angular Elements', () => {
});
it('should recognize the zippy as an Angular Element', () => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-zippy")')
.its('length')
.should('eq', 1);
cy.get('.tree-wrapper').find('.tree-node:contains("app-zippy")').its('length').should('eq', 1);
});
});
@@ -6,29 +6,31 @@
* found in the LICENSE file at https://angular.dev/license
*/
require('cypress-iframe');
describe('Tracking items from application to component tree', () => {
beforeEach(() => {
cy.visit('/');
});
it('should have only one todo item on start', () => {
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo').contains('Buy milk');
});
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.its('length')
.should('eq', 2);
});
it('should be able to detect a new todo from user and add it to the tree', () => {
cy.enterIframe('#sample-app')
cy.enter('#sample-app')
.then((getBody) => {
getBody().find('input.new-todo').type('Buy cookies{enter}');
})
.then(() => {
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo').contains('Buy milk');
getBody().find('app-todo').contains('Build something fun!');
@@ -37,7 +39,7 @@ describe('Tracking items from application to component tree', () => {
});
});
cy.get('.tree-wrapper ng-tree-node:contains("app-todo[TooltipDirective]")').should(
cy.get('.tree-wrapper .tree-node:contains("app-todo[TooltipDirective]")').should(
'have.length',
3,
);
+12 -13
View File
@@ -11,7 +11,7 @@ function checkSearchedNodesLength(type, length) {
}
function inputSearchText(text) {
cy.get('ng-filter .filter-input').type(text, {force: true});
cy.get('.filter-input').type(text, {force: true});
}
function checkComponentName(name) {
@@ -19,7 +19,7 @@ function checkComponentName(name) {
}
function checkEmptyNodes() {
cy.get('.tree-wrapper').find('.matched-text').should('not.exist');
cy.get('.tree-wrapper').find('.matched').should('not.exist');
}
function clickSearchArrows(upwards) {
@@ -44,7 +44,7 @@ describe('Search items in component tree', () => {
it('should highlight correct nodes when searching and clear out', () => {
inputSearchText('todo');
checkSearchedNodesLength('.matched-text', 4);
checkSearchedNodesLength('.matched', 4);
// clear search input
inputSearchText('{backspace}{backspace}{backspace}{backspace}');
@@ -53,23 +53,22 @@ describe('Search items in component tree', () => {
it('should highlight correct nodes when searching and using arrow keys', () => {
inputSearchText('todo');
checkSearchedNodesLength('.matched-text', 4);
checkComponentName('app-todo-demo');
checkSearchedNodesLength('.matched', 4);
// press down arrow
clickSearchArrows(false);
checkSearchedNodesLength('.selected', 1);
checkComponentName('app-todo-demo');
// press up arrow
clickSearchArrows(false);
checkSearchedNodesLength('.selected', 1);
checkComponentName('app-todos');
// press down arrow
clickSearchArrows(false);
checkSearchedNodesLength('.selected', 1);
checkComponentName('app-todo');
// press up arrow
clickSearchArrows(true);
checkSearchedNodesLength('.selected', 1);
checkComponentName('app-todos');
checkComponentName('app-todo-demo');
// clear search input
inputSearchText('{backspace}{backspace}{backspace}{backspace}');
@@ -77,7 +76,7 @@ describe('Search items in component tree', () => {
});
it('should select correct node on enter', () => {
inputSearchText('todo{enter}');
inputSearchText('todos{enter}');
checkSearchedNodesLength('.selected', 1);
// should show correct buttons in breadcrumbs
@@ -92,7 +91,7 @@ describe('Search items in component tree', () => {
checkComponentName('app-todos');
// should display correct title for properties panel
cy.get('ng-property-view-header').should('contain.text', 'app-todos');
cy.get('ng-property-view-header').should('have.text', 'app-todos');
// should show correct component properties
cy.get('ng-property-view').find('mat-tree-node');
@@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
require('cypress-iframe');
describe('node selection', () => {
beforeEach(() => {
cy.visit('/');
@@ -13,55 +15,49 @@ describe('node selection', () => {
describe('logic after change detection', () => {
it('should deselect node if it is no longer on the page', () => {
cy.get('.tree-wrapper')
.get('ng-tree-node:contains("app-todo[TooltipDirective]")')
.should('not.have.class', 'selected');
cy.get('.tree-wrapper').get('.tree-node.selected').should('not.exist');
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.first()
.click({force: true});
cy.get('.tree-wrapper')
.get('ng-tree-node:contains("app-todo[TooltipDirective]")')
.should('have.class', 'selected');
cy.get('.tree-wrapper').find('.tree-node.selected').its('length').should('eq', 1);
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('a:contains("About")').click();
});
cy.get('.tree-wrapper')
.get('ng-tree-node:contains("app-todo[TooltipDirective]")')
.should('not.exist');
cy.get('.tree-wrapper').get('.tree-node.selected').should('not.exist');
});
it('should reselect the previously selected node if it is still present', () => {
cy.get('.tree-wrapper').get('ng-tree-node.selected').should('not.exist');
cy.get('.tree-wrapper').get('.tree-node.selected').should('not.exist');
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('input.new-todo').type('Buy cookies{enter}');
});
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.last()
.click({force: true});
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo:contains("Buy milk")').find('.destroy').click();
});
cy.get('.tree-wrapper').find('ng-tree-node.selected').its('length').should('eq', 1);
cy.get('.tree-wrapper').find('.tree-node.selected').its('length').should('eq', 1);
});
it('should select nodes with same name', () => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.first()
.click({force: true});
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.last()
.click({force: true});
@@ -78,7 +74,7 @@ describe('node selection', () => {
describe('breadcrumb logic', () => {
it('should overflow when breadcrumb list is long enough', () => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("div[TooltipDirective]")')
.find('.tree-node:contains("div[TooltipDirective]")')
.last()
.click({force: true})
.then(() => {
@@ -94,7 +90,7 @@ describe('node selection', () => {
it('should scroll right when right scroll button is clicked', () => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("div[TooltipDirective]")')
.find('.tree-node:contains("div[TooltipDirective]")')
.last()
.click({force: true})
.then(() => {
@@ -110,7 +106,7 @@ describe('node selection', () => {
cy.get('ng-breadcrumbs')
.find('.scroll-button')
.last()
.click({force: true})
.click()
.then(() => {
expect(scrollLeft()).to.be.greaterThan(0);
});
@@ -120,7 +116,7 @@ describe('node selection', () => {
it('should scroll left when left scroll button is clicked', () => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("div[TooltipDirective]")')
.find('.tree-node:contains("div[TooltipDirective]")')
.last()
.click({force: true})
.then(() => {
@@ -136,14 +132,14 @@ describe('node selection', () => {
cy.get('ng-breadcrumbs')
.find('.scroll-button')
.last()
.click({force: true})
.click()
.then(() => {
expect(scrollLeft()).to.be.greaterThan(0);
cy.get('ng-breadcrumbs')
.find('.scroll-button')
.first()
.click({force: true})
.click()
.then(() => {
expect(scrollLeft()).to.eql(0);
});
@@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
require('cypress-iframe');
describe('edit properties of directive in the property view tab', () => {
beforeEach(() => {
cy.visit('/');
@@ -15,13 +17,13 @@ describe('edit properties of directive in the property view tab', () => {
beforeEach(() => {
// select todo node in component tree
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.first()
.click({force: true});
});
it('should be able to enable editMode', () => {
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo input.edit').should('not.be.visible');
});
@@ -34,38 +36,61 @@ describe('edit properties of directive in the property view tab', () => {
.type('true')
.type('{enter}');
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo input.edit').should('be.visible');
});
});
});
describe('edit title property', () => {
beforeEach(() => {
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todos")')
.first()
.click({force: true});
});
it('should change title in app when edited', () => {
cy.enterIframe('#sample-app').then((getBody) => {
getBody().find('#demo-app-title').contains('Angular Todo');
describe('edit todo property', () => {
beforeEach(() => {
// expand todo state
cy.get('.explorer-panel:contains("app-todo")')
.find('ng-property-view mat-tree-node:contains("todo")')
.click();
});
// find title variable and run through edit logic
cy.get('.explorer-panel:contains("app-todos")')
.find('ng-property-view mat-tree-node:contains("title")')
.find('ng-property-editor .editor')
.click()
.find('.editor-input')
.clear()
.type('Hello World')
.type('{enter}');
it('should change todo label in app when edited', () => {
// check initial todo label
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo').contains('Buy milk').its('length').should('eq', 1);
});
// assert that the page has been updated
cy.enterIframe('#sample-app').then((getBody) => {
getBody().find('#demo-app-title').contains('Hello World');
// find label variable and run through edit logic
cy.get('.explorer-panel:contains("app-todo")')
.find('ng-property-view mat-tree-node:contains("label")')
.find('ng-property-editor .editor')
.click()
.find('.editor-input')
.clear()
.type('Buy cookies')
.type('{enter}');
// assert that the page has been updated
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo').contains('Buy cookies').its('length').should('eq', 1);
});
});
it('should change todo completed in app when edited', () => {
// check initial todo completed status
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo li').not('.completed').its('length').should('eq', 2);
});
// find completed variable and run through edit logic
cy.get('.explorer-panel:contains("app-todo")')
.find('ng-property-view mat-tree-node:contains("completed")')
.find('ng-property-editor .editor')
.click()
.find('.editor-input')
.clear()
.type('true')
.type('{enter}');
// assert that the page has been updated
cy.enter('#sample-app').then((getBody) => {
getBody().find('app-todo li.completed').its('length').should('eq', 1);
});
});
});
});
@@ -6,6 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
require('cypress-iframe');
describe('change of the state should reflect in property update', () => {
beforeEach(() => {
cy.visit('/');
@@ -13,13 +15,13 @@ describe('change of the state should reflect in property update', () => {
it('should update the property value', () => {
// Complete the todo
cy.enterIframe('#sample-app').then((getBody) => {
cy.enter('#sample-app').then((getBody) => {
getBody().find('input[type="checkbox"].toggle').first().click();
});
// Select the todo item
cy.get('.tree-wrapper')
.find('ng-tree-node:contains("app-todo[TooltipDirective]")')
.find('.tree-node:contains("app-todo[TooltipDirective]")')
.first()
.click({force: true});
@@ -16,49 +16,49 @@ describe('Viewing component metadata', () => {
cy.visit('/');
});
describe('viewing TodosComponent', () => {
describe('viewing TodoComponent', () => {
beforeEach(() =>
prepareHeaderExpansionPanelForAssertions('ng-tree-node:contains("app-todos")'),
prepareHeaderExpansionPanelForAssertions('.tree-node:contains("app-todo[TooltipDirective]")'),
);
it('should display view encapsulation', () => {
cy.contains('.meta-data-container', 'View Encapsulation: None');
cy.contains('.meta-data-container .mat-button:first', 'View Encapsulation: Emulated');
});
it('should display change detection strategy', () => {
cy.contains('.meta-data-container', 'Change Detection Strategy: Default');
cy.contains('.meta-data-container .mat-button:last', 'Change Detection Strategy: OnPush');
});
});
describe('viewing DemoAppComponent', () => {
beforeEach(() =>
prepareHeaderExpansionPanelForAssertions('ng-tree-node:contains("app-demo-component")'),
prepareHeaderExpansionPanelForAssertions('.tree-node:contains("app-demo-component")'),
);
it('should display view encapsulation', () => {
cy.contains('.meta-data-container', 'View Encapsulation: None');
cy.contains('.meta-data-container .mat-button:first', 'View Encapsulation: None');
});
it('should display change detection strategy', () => {
cy.contains('.meta-data-container', 'Change Detection Strategy: Default');
cy.contains('.meta-data-container .mat-button:last', 'Change Detection Strategy: Default');
});
it('should display correct set of inputs', () => {
cy.contains('.mat-accordion-content#Inputs', 'Inputs');
cy.contains('.mat-accordion-content#Inputs mat-tree-node:first span:first', 'inputOne');
cy.contains('.mat-accordion-content#Inputs mat-tree-node:last span:first', 'inputTwo');
cy.contains('.cy-inputs', 'Inputs');
cy.contains('.cy-inputs mat-tree-node:first span:first', 'inputOne');
cy.contains('.cy-inputs mat-tree-node:last span:first', 'inputTwo');
});
it('should display correct set of outputs', () => {
cy.contains('.mat-accordion-content#Outputs', 'Outputs');
cy.contains('.mat-accordion-content#Outputs mat-tree-node:first span:first', 'outputOne');
cy.contains('.mat-accordion-content#Outputs mat-tree-node:last span:first', 'outputTwo');
cy.contains('.cy-outputs', 'Outputs');
cy.contains('.cy-outputs mat-tree-node:first span:first', 'outputOne');
cy.contains('.cy-outputs mat-tree-node:last span:first', 'outputTwo');
});
it('should display correct set of properties', () => {
cy.contains('.mat-accordion-content#Properties', 'Properties');
cy.contains('.mat-accordion-content#Properties mat-tree-node:first span:first', 'elementRef');
cy.contains('.mat-accordion-content#Properties mat-tree-node:last span:first', 'zippy');
cy.contains('.cy-properties', 'Properties');
cy.contains('.cy-properties mat-tree-node:first span:first', 'elementRef');
cy.contains('.cy-properties mat-tree-node:last span:first', 'zippy');
});
});
});
+25 -22
View File
@@ -6,25 +6,28 @@
* found in the LICENSE file at https://angular.dev/license
*/
/**
* Selects an Iframe and returns its body when it's done loading.
* @param {string} selector - The selector for the iframe.
* @returns {Function} A function that returns the wrapped body of the iframe.
*/
function enterIframe(selector) {
return cy.get(selector, {log: false}).then({timeout: 30000}, async (frame) => {
const contentWindow = frame.prop('contentWindow');
while (
contentWindow.location.toString() === 'about:blank' ||
contentWindow.document.readyState !== 'complete'
) {
await new Promise((resolve) => setTimeout(resolve));
}
// return the body of the iframe wrapped in cypress
return () => cy.wrap(contentWindow.document.body);
});
}
Cypress.Commands.add('enterIframe', enterIframe);
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
@@ -23,7 +23,7 @@
</div>
}
@for (panel of panels(); track $index) {
<div class="mat-accordion-content" [id]="panel.title()" cdkDrag>
<div class="mat-accordion-content" cdkDrag>
@if (panel.controls().dataSource.data.length > 0) {
<mat-expansion-panel [expanded]="true">
<mat-expansion-panel-header collapsedHeight="28px" expandedHeight="28px">
@@ -2,5 +2,4 @@
<app-zippy [title]="getTitle()">This is my content</app-zippy>
<app-heavy></app-heavy>
<div #elementReference>HTMLElement</div>
<div *appStructural>Test Structural Directive</div>
<app-sample-properties></app-sample-properties>
@@ -10,15 +10,11 @@ import {
Component,
computed,
CUSTOM_ELEMENTS_SCHEMA,
Directive,
ElementRef,
inject,
input,
output,
signal,
TemplateRef,
viewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
@@ -27,30 +23,13 @@ import {HeavyComponent} from './heavy.component';
import {SamplePropertiesComponent} from './sample-properties.component';
import {RouterOutlet} from '@angular/router';
// structual directive example
@Directive({
selector: '[appStructural]',
host: {
'[class.app-structural]': 'true',
},
})
export class StructuralDirective {
templateRef = inject(TemplateRef);
viewContainerRef = inject(ViewContainerRef);
ngOnInit() {
// Example of using the structural directive
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
@Component({
selector: 'app-demo-component',
templateUrl: './demo-app.component.html',
styleUrls: ['./demo-app.component.scss'],
encapsulation: ViewEncapsulation.None,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [StructuralDirective, HeavyComponent, SamplePropertiesComponent, RouterOutlet],
imports: [HeavyComponent, SamplePropertiesComponent, RouterOutlet],
})
export class DemoAppComponent {
readonly zippy = viewChild(ZippyComponent);
@@ -4,7 +4,7 @@
<p>{{ 'Sample text processed by a pipe' | sample }}</p>
<section class="todoapp">
<header class="header">
<h1 id="demo-app-title">{{title}}</h1>
<h1>todos</h1>
<input
(keydown.enter)="addTodo(input)"
#input
@@ -28,8 +28,6 @@ const fib = (n: number): number => {
imports: [RouterLink, TodoComponent, TooltipDirective, SamplePipe, TodosFilter],
})
export class TodosComponent implements OnInit, OnDestroy {
title = 'Angular Todo';
todos: Todo[] = [
{
label: 'Buy milk',