Merge pull request #20157 from backstage/rugvip/playwright

replace Cypress with Playwright
This commit is contained in:
Patrik Oldsberg
2023-09-26 11:14:59 +02:00
committed by GitHub
88 changed files with 561 additions and 4662 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/e2e-test-utils': minor
---
Initial release.
+5
View File
@@ -0,0 +1,5 @@
---
'@techdocs/cli': patch
---
Restructured tests.
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/create-app': patch
---
The E2E test setup based on Cypress has been replaced with one based on [Playwright](https://playwright.dev/). Migrating existing apps is not required as this is a standalone setup, only do so if you also want to switch from Cypress to Playwright.
The scripts to run the E2E tests have been removed from `packages/app/package.json`, as they are now instead run from the root. Instead, a new script has been added to the root `package.json`, `yarn test:e2e`, which runs the E2E tests in development mode, unless `CI` is set in the environment.
The Playwright setup uses utilities from the new `@backstage/e2e-test-utils` package to find and include all packages in the monorepo that have an `e2e-tests` folder.
-1
View File
@@ -2,7 +2,6 @@
.yarn/cache
.yarn/install-state.gz
docs
cypress
microsite
node_modules
packages/*/src
+1
View File
@@ -9,3 +9,4 @@
**/microsite/**
**/templates/**
**/sample-templates/**
playwright.config.ts
-1
View File
@@ -8,7 +8,6 @@
yarn.lock @backstage/maintainers @backstage-service
*/yarn.lock @backstage/maintainers @backstage-service
/.changeset/*.md
/cypress/src/integration/plugins/techdocs.spec.ts @backstage/techdocs-maintainers
/docs/assets/search @backstage/discoverability-maintainers
/docs/features/search @backstage/discoverability-maintainers
/docs/features/techdocs @backstage/techdocs-maintainers
+3 -3
View File
@@ -144,15 +144,15 @@ site
# Sensitive credentials
*-credentials.yaml
# e2e tests
cypress/cypress/*
# Possible leftover from build:api-reports
tsconfig.tmp.json
# vscode database functionality support files
*.session.sql
# E2E test reports
e2e-test-report/
# Lighthouse CI Reports
**/.lighthouseci/*
!**/.lighthouseci/scripts
-3
View File
@@ -291,9 +291,6 @@ catalog:
target: ../../plugins/scaffolder-backend/sample-templates/all-templates.yaml
rules:
- allow: [Template]
# Backstage end-to-end tests of TechDocs
- type: file
target: ../../cypress/e2e-fixture.catalog.info.yaml
scaffolder:
# Use to customize default commit author info used when new components are created
# defaultAuthor:
-9
View File
@@ -1,9 +0,0 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
rules: {
'no-console': 0,
'jest/valid-expect': 'off',
'jest/expect-expect': 'off',
'no-restricted-syntax': 'off',
},
};
-2
View File
@@ -1,2 +0,0 @@
registry=https://registry.npmjs.org/
engine-strict=true
-5
View File
@@ -1,5 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
registry "https://registry.npmjs.org/"
network-timeout 300000
-34
View File
@@ -1,34 +0,0 @@
# Cypress Tests for Backstage
Hey 👋 Welcome to the Cypress tests for Backstage. They're designed to be run against the `packages/app` folder in the main repo, and be some form of smoke tests to make sure that we don't break any core functionality.
### Running Locally
In order to make typescript happy, this `cypress` package is separate from all the Jest dependencies in the monorepo workspaces setup.
You can run the e2e tests locally pointing at your local running version like so:
```sh
cd cypress
yarn install
yarn cypress run
```
Note that the tests expect to run against a built application, so you'll want to
run a Docker image and either create an `app-config.cypress.yaml` or pass the
necessary environment variables:
```sh
yarn tsc
yarn build:backend
yarn workspace example-backend build-image
docker run -p 7007:7007 example-backend
```
You can open up the `cypress` console by using `yarn cypress open`.
You can also run towards any Backstage installation by using the Cypress Environment Variable overrides.
```sh
CYPRESS_baseUrl="http://demo.backstage.io" yarn cypress run
```
-10
View File
@@ -1,10 +0,0 @@
{
"baseUrl": "http://localhost:7007",
"integrationFolder": "./src/integration",
"supportFile": "./src/support",
"fixturesFolder": "./src/fixtures",
"pluginsFile": "./src/plugins",
"defaultCommandTimeout": 10000,
"viewportHeight": 900,
"viewportWidth": 1440
}
-11
View File
@@ -1,11 +0,0 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: techdocs-e2e-fixture
description: Used for end-to-end tests of TechDocs in Backstage.
annotations:
backstage.io/techdocs-ref: dir:./fixtures
spec:
type: service
lifecycle: experimental
owner: user:guest
-3
View File
@@ -1,3 +0,0 @@
# Home page
This is a basic documentation used for end-to-end tests.
-109
View File
@@ -1,109 +0,0 @@
# Sub-page 1
## Section 1.1
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 1.2
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 1.3
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
-121
View File
@@ -1,121 +0,0 @@
# Sub-page 3
## Section 3.1
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 3.2
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
### Sub-Section 3.2.1
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
### Sub-Section 3.2.2
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 3.3
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
-146
View File
@@ -1,146 +0,0 @@
# Sub-page 2
## Section 2.1
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 2.2
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
### Sub-Section 2.2.1
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
### Sub-Section 2.2.2
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
## Section 2.3
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
To next page!
[Link to Section 1.1](sub-page-one.md#section-11)
-13
View File
@@ -1,13 +0,0 @@
site_name: e2e Fixture Documentation
site_description: Documentation used for end-to-end tests of TechDocs in Backstage.
repo_url: https://github.com/backstage/backstage
edit_uri: edit/master/cypress/fixtures/docs
nav:
- Home: index.md
- Sub-page 1: sub-page-one.md
- Sub-page 2: sub-page-two.md
- Nested Sub-pages:
- Sub-page 3: sub-page-three.md
plugins:
- techdocs-core
-11
View File
@@ -1,11 +0,0 @@
{
"name": "@backstage/cypress-tests",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"private": true,
"dependencies": {
"cypress": "^12.0.0",
"typescript": "~5.0.0"
}
}
View File
@@ -1,82 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('Integrations', () => {
describe('ReadTree', () => {
it('should work for github', () => {
cy.loginAsGuest();
cy.request('POST', '/api/catalog/locations', {
target:
'https://github.com/backstage-verification/test-repo/blob/main/**/*',
type: 'url',
});
cy.wait(5000);
cy.visit('/catalog');
cy.get('[data-testid="user-picker-all"]').click();
cy.get('table').should('contain', 'github-repo');
cy.get('table').should('contain', 'github-repo-nested');
});
// it('should work for azure', () => {
// cy.loginAsGuest();
// cy.request('POST', '/api/catalog/locations', {
// target:
// 'https://dev.azure.com/backstage-verification/_git/test-repo?path=*',
// type: 'url',
// });
// });
it('should work for gitlab', () => {
cy.loginAsGuest();
cy.request('POST', '/api/catalog/locations', {
target:
'https://gitlab.com/backstage-verification/test-repo/-/tree/master/**/*',
type: 'url',
});
cy.wait(5000);
cy.visit('/catalog');
cy.get('[data-testid="user-picker-all"]').click();
cy.get('table').should('contain', 'gitlab-repo');
cy.get('table').should('contain', 'gitlab-repo-nested');
});
it('should work for bitbucket', () => {
cy.loginAsGuest();
cy.request('POST', '/api/catalog/locations', {
target:
'https://bitbucket.org/backstage-verification/test-repo/src/master/**/*',
type: 'url',
});
cy.wait(5000);
cy.visit('/catalog');
cy.get('[data-testid="user-picker-all"]').click();
cy.get('table').should('contain', 'bitbucket-repo');
cy.get('table').should('contain', 'bitbucket-repo-nested');
});
});
});
@@ -1,199 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('Catalog', () => {
describe('default entities', () => {
it('displays the correct amount of entities from default config', () => {
cy.loginAsGuest();
cy.visit('/catalog');
cy.contains('Owned (10)').should('be.visible');
});
it('Should navigate to a docs tab', () => {
cy.loginAsGuest();
cy.visit('/catalog');
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture',
);
});
cy.getCatalogDocsTab().click();
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Home page')
.should('be.visible');
});
it('Should navigate to a sub-route in docs tab', () => {
cy.loginAsGuest();
cy.visit('/catalog');
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture',
);
});
cy.getCatalogDocsTab().click();
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Home page')
.should('be.visible');
cy.getTechDocsShadowRoot().within(() => {
cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click();
});
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture/docs/sub-page-one/',
);
});
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Sub-page 1')
.should('be.visible');
});
it('Should render addons on docs tab homepage', () => {
cy.loginAsGuest();
cy.visit('/catalog');
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture',
);
});
cy.getCatalogDocsTab().click();
cy.wait(300);
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Home page')
.should('be.visible');
// highlight a snippet of text
cy.getTechDocsShadowRoot()
.find('article > p')
.then($el => {
const el = $el[0];
const document = el.ownerDocument;
const range = document.createRange();
range.selectNodeContents(el);
document?.getSelection()?.removeAllRanges();
document?.getSelection()?.addRange(range);
});
cy.document().trigger('selectionchange');
// wait for new issue default debounce time
cy.wait(600);
// assert that the new issue button has a right url
cy.getTechDocsShadowRoot()
.contains('Open new Github issue')
.should(
'have.attr',
'href',
'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E',
);
});
it('Should render addons on docs tab sup-page', () => {
cy.loginAsGuest();
cy.visit('/catalog');
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture',
);
});
cy.getCatalogDocsTab().click();
cy.wait(300);
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Home page')
.should('be.visible');
cy.getTechDocsShadowRoot().within(() => {
cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click();
});
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/catalog/default/component/techdocs-e2e-fixture/docs/sub-page-one/',
);
});
cy.getTechDocsShadowRoot()
.find('h1')
.contains('Sub-page 1')
.should('be.visible');
// highlight a snippet of text
cy.getTechDocsShadowRoot()
.find('#section-11')
.then($el => {
const el = $el[0];
const document = el.ownerDocument;
const range = document.createRange();
range.selectNodeContents(el);
document?.getSelection()?.removeAllRanges();
document?.getSelection()?.addRange(range);
});
cy.document().trigger('selectionchange');
// wait for new issue default debounce time
cy.wait(600);
// assert that the new issue button has a right url
cy.getTechDocsShadowRoot()
.contains('Open new Github issue')
.should(
'have.attr',
'href',
'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fcatalog%2Fdefault%2Fcomponent%2Ftechdocs-e2e-fixture%2Fdocs%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E',
);
});
});
});
@@ -1,83 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('score-card', () => {
describe('Score board', () => {
it('displays the score board based on sample data', () => {
cy.loginAsGuest();
cy.visit('/score-board');
cy.screenshot({ capture: 'viewport' });
cy.contains('System scores overview').should('be.visible');
cy.checkForErrors();
cy.get('span:contains("1-2 of 2")').should('be.visible'); // beware, there is also a hidden <P/> element
cy.contains('audio-playback').should('be.visible');
cy.contains('team-c').should('be.visible');
cy.contains('non-valid-system').should('be.visible');
cy.contains('Name').should('be.visible');
cy.contains('Date').should('be.visible');
cy.contains('Code').should('be.visible');
cy.contains('Documentation').should('be.visible');
cy.contains('Operations').should('be.visible');
cy.contains('Quality').should('be.visible');
cy.contains('Security').should('be.visible');
cy.contains('Total').should('be.visible');
cy.contains('50 %').should('be.visible');
cy.contains('75 %').should('be.visible');
cy.log('navigating to score card detail for audio-playback');
cy.get('a[data-id="audio-playback"]').should('be.visible').click();
cy.screenshot({ capture: 'viewport' });
cy.url().should(
'include',
'/catalog/default/System/audio-playback/score',
);
cy.contains('Scoring').should('be.visible');
cy.contains('Total score: 57 %').should('be.visible');
cy.contains('Code').should('be.visible');
cy.contains('90 %').should('be.visible');
cy.contains('Documentation').should('be.visible');
cy.contains('75 %').should('be.visible');
cy.contains('Operations').should('be.visible');
cy.contains('50 %').should('be.visible');
cy.contains('Quality').should('be.visible');
cy.contains('25 %').should('be.visible');
cy.contains('Security');
cy.contains('10 %').should('be.visible');
cy.checkForErrors();
cy.log(
'Clicking on button [>] that is first child of the element (td) with value=Code',
);
cy.get('[value="Code"] > button:first-child').click();
cy.checkForErrors();
cy.screenshot({ capture: 'viewport' });
cy.log('Clicking on link for Code');
cy.contains('hints: Gitflow: 100%').should('be.visible');
cy.get('a[data-id="2157"]')
.should('be.visible')
.should(
'have.attr',
'href',
'https://TBD/XXX/_wiki/wikis/XXX.wiki/2157',
);
});
});
});
@@ -1,259 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('TechDocs', () => {
beforeEach(() => {
cy.loginAsGuest();
cy.mockSockJSNode();
cy.interceptTechDocsAPICalls();
});
describe('Navigating to TechDocs', () => {
it('should navigate to the TechDocs home page via the primary navigation bar', () => {
cy.visit('/');
cy.wait(500);
cy.get('[data-testid="sidebar-root"]')
.get('div')
.get('a[href="/docs"]')
.click();
cy.contains('Documentation');
});
it('should navigate to the TechDocs home page from the URL', () => {
cy.visit('/docs');
cy.wait(500);
cy.contains('Documentation');
});
it('should navigate to a specific TechDocs entity from the "Overview" tab', () => {
cy.visit('/docs');
cy.contains('techdocs-e2e-fixture').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/docs/default/component/techdocs-e2e-fixture',
);
});
});
it('should navigate to a specific TechDocs entity page from a URL', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.waitHomePage();
cy.contains('e2e Fixture Documentation');
cy.contains(
'Documentation used for end-to-end tests of TechDocs in Backstage.',
);
cy.getTechDocsShadowRoot().contains('Home page');
});
it('should navigate to a specific TechDocs section from a URL', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture/sub-page-two');
cy.waitSectionTwoPage();
cy.window().its('scrollY').should('equal', 0);
cy.getTechDocsShadowRoot().within(() => {
cy.contains('Sub-page 2');
});
});
it('should navigate to a specific TechDocs fragment from a URL', () => {
cy.visit(
'/docs/default/Component/techdocs-e2e-fixture/sub-page-two#section-23',
);
// This is used to test the post-render behavior of the techdocs Reader
cy.wait(500);
cy.getTechDocsShadowRoot().within(() => {
cy.isInViewport('#section-23');
});
});
it('should navigate to a wrong TechDocs entity page from a URL', () => {
cy.visit('/docs/default/Component/wrong-component');
cy.get('[data-testid=error]').should('be.visible');
});
});
describe('Rendering TechDocs Addons', () => {
it('should render a content addon in homepage', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.contains('e2e Fixture Documentation');
// highlight a snippet of text
cy.getTechDocsShadowRoot()
.find('article > p')
.then($el => {
const el = $el[0];
const document = el.ownerDocument;
const range = document.createRange();
range.selectNodeContents(el);
document?.getSelection()?.removeAllRanges();
document?.getSelection()?.addRange(range);
});
cy.document().trigger('selectionchange');
// wait for new issue default debounce time
cy.wait(600);
// assert that the new issue button has a right url
cy.getTechDocsShadowRoot()
.contains('Open new Github issue')
.should(
'have.attr',
'href',
'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20This%20is%20a%20basic%20documentation%20used%20for%20end-to-end%20tests.%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Findex.md%3E',
);
});
it('should render a content addon in sub-pages', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.contains('e2e Fixture Documentation');
// open sub-page
cy.getTechDocsShadowRoot().within(() => {
cy.getTechDocsNavigation().find('a').contains('Sub-page 1').click();
});
// highlight a snippet of text
cy.getTechDocsShadowRoot()
.find('#section-11')
.then($el => {
const el = $el[0];
const document = el.ownerDocument;
const range = document.createRange();
range.selectNodeContents(el);
document?.getSelection()?.removeAllRanges();
document?.getSelection()?.addRange(range);
});
cy.document().trigger('selectionchange');
// wait for new issue default debounce time
cy.wait(600);
// assert that the new issue button has a right url
cy.getTechDocsShadowRoot()
.contains('Open new Github issue')
.should(
'have.attr',
'href',
'https://github.com/backstage/backstage/issues/new?title=Documentation%20feedback%3A%20Section%201.1%C2%B6&body=%23%23%20Documentation%20Feedback%20%F0%9F%93%9D%0A%0A%20%23%23%23%23%20The%20highlighted%20text%3A%20%0A%0A%20%3E%20Section%201.1%C2%B6%0A%0A%20%23%23%23%23%20The%20comment%20on%20the%20text%3A%20%0A%20_%3Ereplace%20this%20line%20with%20your%20comment%3C_%0A%0A%20___%0ABackstage%20URL%3A%20%3Chttp%3A%2F%2Flocalhost%3A7007%2Fdocs%2Fdefault%2FComponent%2Ftechdocs-e2e-fixture%2Fsub-page-one%2F%3E%20%0AMarkdown%20URL%3A%20%3Chttps%3A%2F%2Fgithub.com%2Fbackstage%2Fbackstage%2Fblob%2Fmaster%2Fcypress%2Ffixtures%2Fdocs%2Fsub-page-one.md%3E',
);
});
});
describe('Navigating within TechDocs', () => {
it('should navigate to a specific TechDocs page via the navigation bar', () => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture');
cy.getTechDocsShadowRoot().within(() => {
cy.getTechDocsNavigation()
.find('> div > div > [data-md-level="0"] > ul > li:nth-child(2) > a')
.click();
cy.contains('Sub-page 1');
cy.window().its('scrollY').should('eq', 0);
});
});
describe('Navigating within a TechDocs page', () => {
beforeEach(() => {
cy.visit('/docs/default/Component/techdocs-e2e-fixture/sub-page-two');
});
it('should navigate to a specific fragment within the page via the table of contents - Level 1', () => {
return cy.getTechDocsShadowRoot().within(() => {
// Section 3
cy.getTechDocsTableOfContents()
.find('a')
.contains('Section 2.3')
.click();
// wait scroll timeout
cy.wait(500);
cy.isInViewport('#section-23');
});
});
it('should navigate to a specific fragment within the page via the table of contents - Level 2', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.isNotInViewport('#sub-section-222');
// Section 2.2
cy.getTechDocsTableOfContents()
.find('a')
.contains('Section 2.2.2')
.click();
// wait scroll timeout
cy.wait(500);
cy.isInViewport('#sub-section-222');
});
});
it('should navigate to a specific TechDocs page fragment from a link', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('article').contains('Link to Section 1.1').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/docs/default/Component/techdocs-e2e-fixture/sub-page-one/',
);
expect(loc.hash).to.eq('#section-11');
});
});
});
it('should navigate to the next page within a TechDocs entity', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('footer a').contains('Sub-page 3').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/docs/default/Component/techdocs-e2e-fixture/sub-page-three/',
);
});
});
});
it('should navigate to the previous page within a TechDocs entity', () => {
return cy.getTechDocsShadowRoot().within(() => {
cy.get('footer a').contains('Sub-page 1').click();
cy.location().should(loc => {
expect(loc.pathname).to.eq(
'/docs/default/Component/techdocs-e2e-fixture/sub-page-one/',
);
});
});
});
});
});
});
@@ -1,35 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('Logout', () => {
before(() => {
cy.loginAsGuest();
});
it('should be able to logout', () => {
cy.visit('/settings');
cy.get('[data-testid="user-settings-menu"]').click();
return cy
.get('[data-testid="sign-out"]')
.click()
.then(() => {
return expect(
localStorage.getItem('@backstage/core:SignInPage:provider'),
).to.be.null;
});
});
});
-125
View File
@@ -1,125 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable jest/no-standalone-expect */
/// <reference types="cypress" />
import 'os';
Cypress.Commands.add('loginAsGuest', () => {
cy.visit('/', {
onLoad: (win: Window) =>
win.localStorage.setItem('@backstage/core:SignInPage:provider', 'guest'),
});
});
Cypress.Commands.add('getTechDocsShadowRoot', () => {
cy.get('[data-testid="techdocs-native-shadowroot"]').shadow();
});
Cypress.Commands.add('isNotInViewport', element => {
cy.get(element).then($el => {
const bottom = Cypress.config(`viewportHeight`);
const rect = $el[0].getBoundingClientRect();
if (bottom) {
expect(rect.top).to.be.greaterThan(bottom);
expect(rect.bottom).to.be.greaterThan(bottom);
expect(rect.top).to.be.greaterThan(bottom);
expect(rect.bottom).to.be.greaterThan(bottom);
}
});
});
Cypress.Commands.add('isInViewport', element => {
cy.get(element).then($el => {
const bottom = Cypress.config(`viewportHeight`);
const rect = $el[0].getBoundingClientRect();
if (bottom) {
expect(rect.top).not.to.be.greaterThan(bottom);
expect(rect.bottom).not.to.be.greaterThan(bottom);
expect(rect.top).not.to.be.greaterThan(bottom);
expect(rect.bottom).not.to.be.greaterThan(bottom);
}
});
});
Cypress.Commands.add('getTechDocsTableOfContents', () => {
cy.get('[data-md-type="toc"]');
});
Cypress.Commands.add('getTechDocsNavigation', () => {
cy.get('[data-md-type="navigation"]');
});
Cypress.Commands.add('getCatalogDocsTab', () => {
cy.get('button[role="tab"]').contains('Docs');
});
Cypress.Commands.add('mockSockJSNode', () => {
cy.intercept('GET', '**/sockjs-node/info**', {
body: {
websocket: true,
origins: ['*:*'],
cookie_needed: false,
entropy: 2882389500,
},
});
});
Cypress.Commands.add('interceptTechDocsAPICalls', () => {
cy.intercept(
'GET',
'**/techdocs/metadata/entity/default/Component/techdocs-e2e-fixture',
).as('entityMetadata');
cy.intercept(
'GET',
'**/techdocs/metadata/techdocs/default/Component/techdocs-e2e-fixture',
).as('techdocsMetadata');
cy.intercept(
'GET',
'**/techdocs/sync/default/Component/techdocs-e2e-fixture',
).as('syncEntity');
cy.intercept(
'GET',
'**/techdocs/static/docs/default/Component/techdocs-e2e-fixture/sub-page-two/index.html',
).as('sectionTwoHTML');
cy.intercept(
'GET',
'**/techdocs/static/docs/default/Component/techdocs-e2e-fixture/index.html',
).as('homeHTML');
});
Cypress.Commands.add('waitSectionTwoPage', () => {
cy.wait([
'@entityMetadata',
'@syncEntity',
'@techdocsMetadata',
'@sectionTwoHTML',
]);
});
Cypress.Commands.add('waitHomePage', () => {
cy.wait(['@entityMetadata', '@syncEntity', '@techdocsMetadata', '@homeHTML']);
});
Cypress.Commands.add('checkForErrors', () => {
// when an error occurs there is a <div> with an "alert" role attribute. This can change ofc => we shall add also some positive ("when error occurs") test
cy.get('div[role="alert"]').should('not.exist');
});
-86
View File
@@ -1,86 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare module 'zombie';
declare module 'pgtools';
declare namespace Cypress {
interface Chainable {
/**
* Login as guest
* @example cy.loginAsGuests
*/
loginAsGuest(): Chainable<Element>;
/**
* Get the TechDocs shadow root element
* @example cy.getTechDocsShadowRoot
*/
getTechDocsShadowRoot(): Chainable<Element>;
/**
* Mock TechDocs backend API
* @example cy.mockTechDocs
*/
mockTechDocs(): void;
/**
* Get the TechDocs table of contents element
* @example cy.getTechDocsShadowRoot
*/
getTechDocsTableOfContents(): Chainable<Element>;
/**
* Get the TechDocs navigation element
* @example cy.getTechDocsNavigation
*/
getTechDocsNavigation(): Chainable<Element>;
/**
* Get the Catalog docs tab
* @example cy.getCatalogDocsTab
*/
getCatalogDocsTab(): Chainable<Element>;
/**
* Intercept the TechDocs API calls
* @example cy.interceptTechDocsAPICalls
*/
interceptTechDocsAPICalls(): Chainable<Element>;
/**
* Mock SockJS-Node call
* @example cy.mockSockJSNode
*/
mockSockJSNode(): Chainable<Element>;
/**
* Wait TechDocs API response for home page
* @example cy.waitHomePage
*/
waitHomePage(): Chainable<Element>;
/**
* Wait TechDocs API response for Section 2 page
* @example cy.waitSectionTwoPage
*/
waitSectionTwoPage(): Chainable<Element>;
/**
* Check if the element is in viewport
* @example cy.isInViewport
*/
isInViewport(element: string): Chainable<Element>;
/**
* Check if the element is not in viewport
* @example cy.isNotInViewport
*/
isNotInViewport(element: string): Chainable<Element>;
/**
* Check if we have not caused error by our last action
* @example cy.checkForErrors
*/
checkForErrors(): Chainable<Element>;
}
}
-52
View File
@@ -1,52 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ***********************************************
// This example commands.ts 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) => { ... })
//
// declare global {
// namespace Cypress {
// interface Chainable {
// login(email: string, password: string): Chainable<void>
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
// }
// }
// }
-35
View File
@@ -1,35 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This example support/e2e.ts is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
-37
View File
@@ -1,37 +0,0 @@
{
"compilerOptions": {
"allowJs": true,
"declaration": true,
"declarationMap": false,
"esModuleInterop": true,
"experimentalDecorators": false,
"forceConsistentCasingInFileNames": true,
"importHelpers": false,
"incremental": true,
"isolatedModules": true,
"jsx": "react",
"lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2021", "ESNext.Promise"],
"module": "ESNext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"pretty": true,
"removeComments": false,
"resolveJsonModule": true,
"sourceMap": false,
"skipLibCheck": false,
"strict": true,
"strictBindCallApply": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"strictPropertyInitialization": true,
"stripInternal": true,
"target": "ES2021",
"types": ["node", "cypress"]
}
}
-1639
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -18,6 +18,7 @@
"clean": "backstage-cli repo clean",
"test": "backstage-cli repo test",
"test:all": "backstage-cli repo test --coverage",
"test:e2e": "playwright test",
"fix": "backstage-cli repo fix",
"lint": "backstage-cli repo lint --since origin/master",
"lint:docs": "node ./scripts/check-docs-quality",
@@ -58,9 +59,11 @@
"@backstage/cli": "workspace:*",
"@backstage/codemods": "workspace:*",
"@backstage/create-app": "workspace:*",
"@backstage/e2e-test-utils": "workspace:*",
"@backstage/repo-tools": "workspace:*",
"@changesets/cli": "^2.14.0",
"@octokit/rest": "^19.0.3",
"@playwright/test": "^1.32.3",
"@spotify/eslint-plugin": "^14.1.3",
"@spotify/prettier-config": "^14.0.0",
"@techdocs/cli": "workspace:*",
+2 -10
View File
@@ -99,7 +99,6 @@
},
"devDependencies": {
"@backstage/test-utils": "workspace:^",
"@testing-library/cypress": "^9.0.0",
"@testing-library/dom": "^8.0.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
@@ -108,21 +107,14 @@
"@types/react": "*",
"@types/react-dom": "*",
"@types/zen-observable": "^0.8.0",
"cross-env": "^7.0.0",
"cypress": "^12.0.0",
"eslint-plugin-cypress": "^2.10.3",
"start-server-and-test": "^1.10.11"
"cross-env": "^7.0.0"
},
"scripts": {
"start": "backstage-cli package start --config ../../app-config.yaml --config app-config.yaml",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint",
"test:e2e": "start-server-and-test start http://localhost:3000 cy:dev",
"test:e2e:ci": "start-server-and-test start http://localhost:3000 cy:run",
"cy:dev": "cypress open",
"cy:run": "cypress run"
"lint": "backstage-cli package lint"
},
"browserslist": {
"production": [
-5
View File
@@ -1,5 +0,0 @@
{
"baseUrl": "http://localhost:3000",
"fixturesFolder": false,
"pluginsFile": false
}
-12
View File
@@ -1,12 +0,0 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": [
"error",
{
"assertFunctionNames": ["expect", "cy.contains", "cy.**.should"]
}
]
}
}
-67
View File
@@ -1,67 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
describe('App', () => {
it('should render the welcome page', () => {
cy.visit('/');
cy.contains('Welcome to Backstage');
cy.contains('Getting Started');
cy.contains('Quick Links');
cy.contains('APIs');
});
it('should display support info when clicking the button', () => {
cy.visit('/');
// eslint-disable-next-line testing-library/await-async-query
cy.findByTestId('support-button').click({ force: true });
cy.contains('#backstage');
});
it('should display error message when triggering it', () => {
cy.visit('/');
// eslint-disable-next-line testing-library/await-async-query
cy.findByTestId('error-button').click({ force: true });
cy.contains('Error: Oh no!');
// eslint-disable-next-line testing-library/await-async-query
cy.findByTestId('error-button-close').click({ force: true });
});
it('should be able to login and logout', () => {
const name = 'test-name';
Cypress.on('window:before:load', win => {
win.fetch = cy.stub().resolves({
status: 200,
json: () => ({ username: 'test name', token: 'token', name }),
});
});
cy.visit('/');
cy.get('a[href="/login"]').click({ force: true });
cy.url().should('include', '/login');
cy.contains('Welcome, guest!');
cy.contains('Username')
.get('input[name=github-username-tf]')
.type(name, { force: true });
cy.contains('Token')
.get('input[name=github-auth-tf]')
.type('password', { force: true });
// eslint-disable-next-line testing-library/await-async-query
cy.findByTestId('github-auth-button').click({ force: true });
cy.contains(`Welcome, ${name}!`);
cy.contains('Logout').click({ force: true });
cy.contains('Welcome, guest!');
});
});
@@ -1,121 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const API_ENDPOINT = 'http://localhost:7007/api/search/query';
describe('SearchPage', () => {
describe('Given a search context with a term, results, and filter values', () => {
it('The results are rendered as expected', () => {
const results = [
{
type: 'software-catalog',
document: {
title: 'backstage',
text: 'Backstage system documentation',
location: '/result/location/path',
},
},
];
cy.enterAsGuest();
cy.visit('/search-next', {
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(`${API_ENDPOINT}?term=`)
.resolves({
ok: true,
json: () => ({ results }),
});
},
});
cy.contains('Search');
cy.contains(results[0].document.title);
cy.contains(results[0].document.text);
cy.get(`a[href="${results[0].document.location}"]`).should('be.visible');
});
it('The filters are rendered as expected', () => {
cy.enterAsGuest();
cy.visit(
'/search-next?filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B%5D=experimental',
{
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(
`${API_ENDPOINT}?term=&filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B0%5D=experimental`,
)
.resolves({
ok: true,
json: () => ({ results: [] }),
});
},
},
);
cy.contains('Search');
// lifecycle
cy.contains('lifecycle');
cy.contains('experimental');
cy.get(
'[data-testid="search-checkboxfilter-next"] input[value="experimental"]',
).should('have.attr', 'checked');
cy.contains('production');
cy.get(
'[data-testid="search-checkboxfilter-next"] input[value="production"]',
).should('not.have.attr', 'checked');
// kind
cy.contains('kind');
cy.get(
'[data-testid="search-selectfilter-next"] [role="button"][aria-haspopup="listbox"]',
).click();
cy.contains('All');
cy.contains('Template');
cy.contains('Component');
cy.get('[role="option"][data-value="Component"]').should(
'have.attr',
'aria-selected',
'true',
);
});
it('The search bar is rendered as expected', () => {
cy.enterAsGuest();
cy.visit('/search-next?query=backstage', {
onBeforeLoad(win) {
cy.stub(win, 'fetch')
.withArgs(`${API_ENDPOINT}?term=backstage`)
.resolves({
ok: true,
json: () => ({ results: [] }),
});
},
});
cy.contains('Search');
cy.get('[data-testid="search-bar-next"] input').should(
'have.attr',
'value',
'backstage',
);
});
});
});
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test, expect } from '@playwright/test';
test('the results are rendered as expected', async ({ page }) => {
await page.goto('/');
const enterButton = page.getByRole('button', { name: 'Enter' });
await expect(enterButton).toBeVisible();
await enterButton.click();
await page.goto('/search');
await page.route(`http://*/api/search/query?term=*`, async route => {
const results = [
{
type: 'software-catalog',
document: {
title: 'backstage',
text: 'Backstage system documentation',
location: '/result/location/path',
},
},
];
await route.fulfill({ json: { results } });
});
await expect(
page.getByPlaceholder('Search in Backstage Example App'),
).toBeVisible();
await expect(page.getByText('Backstage system documentation')).toBeVisible();
});
@@ -13,19 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import 'os';
describe('Login', () => {
it('should render the login page', () => {
cy.visit('/');
cy.contains('Select a sign-in method');
});
import { test, expect } from '@playwright/test';
it('should be able to login', () => {
cy.get('button').contains('Enter').click();
cy.url().should('include', '/catalog');
test('App should render the welcome page', async ({ page }) => {
await page.goto('/');
cy.contains('artist-lookup');
});
const enterButton = page.getByRole('button', { name: 'Enter' });
await expect(enterButton).toBeVisible();
await enterButton.click();
await expect(page.getByText('My Company Catalog')).toBeVisible();
const supportButton = page.getByTestId('support-button');
await expect(supportButton).toBeVisible();
await supportButton.click();
await expect(page.getByText('#backstage')).toBeVisible();
});
+3 -10
View File
@@ -103,7 +103,7 @@
},
"devDependencies": {
"@backstage/test-utils": "workspace:^",
"@testing-library/cypress": "^9.0.0",
"@playwright/test": "^1.32.3",
"@testing-library/dom": "^8.0.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
@@ -112,21 +112,14 @@
"@types/react": "*",
"@types/react-dom": "*",
"@types/zen-observable": "^0.8.0",
"cross-env": "^7.0.0",
"cypress": "^12.0.0",
"eslint-plugin-cypress": "^2.10.3",
"start-server-and-test": "^1.10.11"
"cross-env": "^7.0.0"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint",
"test:e2e": "start-server-and-test start http://localhost:3000 cy:dev",
"test:e2e:ci": "start-server-and-test start http://localhost:3000 cy:run",
"cy:dev": "cypress open",
"cy:run": "cypress run"
"lint": "backstage-cli package lint"
},
"browserslist": {
"production": [
@@ -66,6 +66,7 @@ jest.mock('./versions', () => ({
'@backstage/core-app-api': '1.0.0',
'@backstage/core-components': '1.0.0',
'@backstage/core-plugin-api': '1.0.0',
'@backstage/e2e-test-utils': '1.0.0',
'@backstage/integration-react': '1.0.0',
'@backstage/plugin-api-docs': '1.0.0',
'@backstage/plugin-catalog': '1.0.0',
+2
View File
@@ -41,6 +41,7 @@ import { version as config } from '../../../config/package.json';
import { version as coreAppApi } from '../../../core-app-api/package.json';
import { version as coreComponents } from '../../../core-components/package.json';
import { version as corePluginApi } from '../../../core-plugin-api/package.json';
import { version as e2eTestUtils } from '../../../e2e-test-utils/package.json';
import { version as errors } from '../../../errors/package.json';
import { version as integrationReact } from '../../../integration-react/package.json';
import { version as testUtils } from '../../../test-utils/package.json';
@@ -92,6 +93,7 @@ export const packageVersions = {
'@backstage/core-app-api': coreAppApi,
'@backstage/core-components': coreComponents,
'@backstage/core-plugin-api': corePluginApi,
'@backstage/e2e-test-utils': e2eTestUtils,
'@backstage/errors': errors,
'@backstage/integration-react': integrationReact,
'@backstage/plugin-api-docs': pluginApiDocs,
@@ -49,3 +49,6 @@ site
# vscode database functionality support files
*.session.sql
# E2E test reports
e2e-test-report/
@@ -17,6 +17,7 @@
"clean": "backstage-cli repo clean",
"test": "backstage-cli repo test",
"test:all": "backstage-cli repo test --coverage",
"test:e2e": "playwright test",
"fix": "backstage-cli repo fix",
"lint": "backstage-cli repo lint --since origin/{{defaultBranch}}",
"lint:all": "backstage-cli repo lint",
@@ -31,6 +32,8 @@
},
"devDependencies": {
"@backstage/cli": "^{{version '@backstage/cli'}}",
"@backstage/e2e-test-utils": "^{{version '@backstage/e2e-test-utils'}}",
"@playwright/test": "^1.32.3",
"@spotify/prettier-config": "^12.0.0",
"concurrently": "^8.0.0",
"lerna": "^4.0.0",
@@ -1,11 +0,0 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
fixturesFolder: false,
retries: 3,
e2e: {
supportFile: false,
baseUrl: 'http://localhost:3001',
},
});
@@ -1,12 +0,0 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": [
"error",
{
"assertFunctionNames": ["expect", "cy.contains", "cy.**.should"]
}
]
}
}
@@ -1,6 +0,0 @@
describe('App', () => {
it('should render the catalog', () => {
cy.visit('/');
cy.contains('My Company Catalog');
});
});
@@ -14,9 +14,10 @@
* limitations under the License.
*/
describe('App', () => {
it('should render the catalog', () => {
cy.visit('/');
cy.contains('My Company Service Catalog');
});
import { test, expect } from '@playwright/test';
test('App should render the welcome page', async ({ page }) => {
await page.goto('/');
await expect(page.getByText('My Company Catalog')).toBeVisible();
});
@@ -11,11 +11,7 @@
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint",
"test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev",
"test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run",
"cy:dev": "cypress open",
"cy:run": "cypress run --browser chrome"
"lint": "backstage-cli package lint"
},
"dependencies": {
"@backstage/app-defaults": "^{{version '@backstage/app-defaults'}}",
@@ -54,15 +50,13 @@
},
"devDependencies": {
"@backstage/test-utils": "^{{version '@backstage/test-utils'}}",
"@playwright/test": "^1.32.3",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@testing-library/dom": "^8.0.0",
"@types/react-dom": "*",
"cross-env": "^7.0.0",
"cypress": "^12.0.0",
"eslint-plugin-cypress": "^2.10.3",
"start-server-and-test": "^1.10.11"
"cross-env": "^7.0.0"
},
"browserslist": {
"production": [
@@ -0,0 +1,60 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineConfig } from '@playwright/test';
import { generateProjects } from '@backstage/e2e-test-utils/playwright';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
timeout: 60_000,
expect: {
timeout: 5_000,
},
// Run your local dev server before starting the tests
webServer: process.env.CI
? []
: [
{
command: 'yarn start',
port: 3000,
reuseExistingServer: true,
timeout: 60_000,
},
],
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['html', { open: 'never', outputFolder: 'e2e-test-report' }]],
use: {
actionTimeout: 0,
baseURL:
process.env.PLAYWRIGHT_URL ??
(process.env.CI ? 'http://localhost:7007' : 'http://localhost:3000'),
screenshot: 'only-on-failure',
trace: 'on-first-retry',
},
outputDir: 'node_modules/.cache/e2e-test-results',
projects: generateProjects(), // Find all packages with e2e-test folders
});
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+12
View File
@@ -0,0 +1,12 @@
# @backstage/e2e-test-utils
_This package was created through the Backstage CLI_.
## Installation
Install the package via Yarn:
```sh
cd <package-dir> # if within a monorepo
yarn add --dev @backstage/e2e-test-utils
```
+10
View File
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-e2e-test-utils
title: '@backstage/e2e-test-utils'
description: Shared end-to-end test utilities Backstage
spec:
lifecycle: experimental
type: backstage-node-library
owner: maintainers
+57
View File
@@ -0,0 +1,57 @@
{
"name": "@backstage/e2e-test-utils",
"description": "Shared end-to-end test utilities Backstage",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"exports": {
"./playwright": "./src/playwright/index.ts",
"./package.json": "./package.json"
},
"typesVersions": {
"*": {
"playwright": [
"src/playwright/index.ts"
],
"package.json": [
"package.json"
]
}
},
"backstage": {
"role": "node-library"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@manypkg/get-packages": "^1.1.3",
"fs-extra": "^10.1.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/cli-node": "workspace:^",
"@types/fs-extra": "^9.0.1"
},
"peerDependencies": {
"@playwright/test": "^1.32.3"
},
"peerDependenciesMeta": {
"@playwright/test": {
"optional": true
}
},
"files": [
"dist"
]
}
@@ -0,0 +1,15 @@
## API Report File for "@backstage/e2e-test-utils"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { PlaywrightTestConfig } from '@playwright/test';
// @public
export function failOnBrowserErrors(): void;
// @public
export function generateProjects(): PlaywrightTestConfig['projects'];
// (No @packageDocumentation comment for this package)
```
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,4 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export default () => {};
export {};
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,5 +13,5 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
import './commands';
export * from './playwright/index';
@@ -0,0 +1,37 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { test } from '@playwright/test';
/**
* Calling this at the top of your test file will ensure that tests fail if the browser
* logs any errors or if there are any uncaught exceptions.
*
* @public
*/
export function failOnBrowserErrors(): void {
test.beforeEach(async ({ page }) => {
page.on('pageerror', error => {
throw new Error(`Uncaught exception on page, ${error}`);
});
page.on('console', msg => {
if (msg.type() === 'error') {
throw new Error(`Unexpected console error message "${msg.text()}"`);
}
});
});
}
@@ -0,0 +1,42 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { PlaywrightTestConfig } from '@playwright/test';
import { getPackagesSync } from '@manypkg/get-packages';
import type { BackstagePackage } from '@backstage/cli-node';
/**
* Generates a list of playwright projects by scanning the monorepo for packages with an `e2e-tests/` folder.
*
* @public
*/
export function generateProjects(): PlaywrightTestConfig['projects'] {
// TODO(Rugvip): Switch this over to use @backstage/cli-node once released, and support SINCE=origin/main
const { root, packages } = getPackagesSync(process.cwd());
const e2eTestPackages = [...(root ? [root] : []), ...packages].filter(pkg => {
return fs.pathExistsSync(resolvePath(pkg.dir, 'e2e-tests'));
}) as BackstagePackage[];
return e2eTestPackages.map(pkg => ({
name: pkg.packageJson.name,
testDir: resolvePath(pkg.dir, 'e2e-tests'),
use: {
channel: 'chrome',
},
}));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2021 The Backstage Authors
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Cypress.Commands.add('enterAsGuest', () => {
cy.visit('/');
cy.get('button').contains('Enter').click();
});
export { generateProjects } from './generateProjects';
export { failOnBrowserErrors } from './failOnBrowserErrors';
@@ -1,5 +1,5 @@
/*
* Copyright 2020 The Backstage Authors
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,5 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import '@testing-library/cypress/add-commands';
import './commands';
export {};
-1
View File
@@ -37,7 +37,6 @@
"handlebars": "^4.7.3",
"mysql2": "^2.2.5",
"pgtools": "^1.0.0",
"puppeteer": "^17.0.0",
"tree-kill": "^1.2.2"
},
"devDependencies": {
+6 -70
View File
@@ -20,13 +20,11 @@ import fetch from 'cross-fetch';
import handlebars from 'handlebars';
import killTree from 'tree-kill';
import { resolve as resolvePath, join as joinPath } from 'path';
import puppeteer from 'puppeteer';
import path from 'path';
import {
spawnPiped,
runPlain,
waitForPageWithText,
waitFor,
waitForExit,
print,
@@ -42,6 +40,7 @@ const paths = findPaths(__dirname);
const templatePackagePaths = [
'packages/cli/templates/default-plugin/package.json.hbs',
'packages/create-app/templates/default-app/package.json.hbs',
'packages/create-app/templates/default-app/packages/app/package.json.hbs',
'packages/create-app/templates/default-app/packages/backend/package.json.hbs',
];
@@ -66,8 +65,11 @@ export async function run() {
print('Creating a Backstage Backend Plugin');
await createPlugin({ appDir, pluginId, select: 'backend-plugin' });
print('Starting the app');
await testAppServe(pluginId, appDir);
print(`Running 'yarn test:e2e' in newly created app with new plugin`);
await runPlain(['yarn', 'test:e2e'], {
cwd: appDir,
env: { ...process.env, CI: undefined },
});
if (
Boolean(process.env.POSTGRES_USER) ||
@@ -297,15 +299,6 @@ async function createApp(
await runPlain(['yarn', cmd], { cwd: appDir });
}
print(`Running 'yarn test:e2e:ci' in newly created app`);
await runPlain(['yarn', 'test:e2e:ci'], {
cwd: resolvePath(appDir, 'packages', 'app'),
env: {
...process.env,
APP_CONFIG_app_baseUrl: '"http://localhost:3001"',
},
});
return appDir;
} finally {
child.kill();
@@ -381,63 +374,6 @@ async function createPlugin(options: {
}
}
/**
* Start serving the newly created app and make sure that the create plugin is rendering correctly
*/
async function testAppServe(pluginId: string, appDir: string) {
const startApp = spawnPiped(['yarn', 'start'], {
cwd: appDir,
env: {
...process.env,
GITHUB_TOKEN: 'abc',
},
});
let successful = false;
let browser;
try {
for (let attempts = 1; ; attempts++) {
try {
browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:3000', { waitUntil: 'networkidle0' });
await waitForPageWithText(page, '/', 'My Company Catalog');
await waitForPageWithText(
page,
`/${pluginId}`,
`Welcome to ${pluginId}!`,
);
print('Both App and Plugin loaded correctly');
successful = true;
break;
} catch (error) {
if (attempts >= 20) {
throw new Error(`App serve test failed, ${error}`);
}
print(`App serve failed, trying again, ${error}`);
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
} finally {
// Kill entire process group, otherwise we'll end up with hanging serve processes
await new Promise<void>((res, rej) => {
killTree(startApp.pid!, err => (err ? rej(err) : res()));
});
}
try {
await waitForExit(startApp);
} catch (error) {
if (!successful) {
throw error;
}
}
}
/** Drops PG databases */
async function dropDB(database: string, client: string) {
try {
-45
View File
@@ -22,7 +22,6 @@ import {
ChildProcess,
} from 'child_process';
import { promisify } from 'util';
import puppeteer from 'puppeteer';
const execFile = promisify(execFileCb);
@@ -125,50 +124,6 @@ export async function waitForExit(child: ChildProcess) {
);
}
export async function waitForPageWithText(
page: puppeteer.Page,
path: string,
text: string,
{ intervalMs = 1000, maxFindAttempts = 50 } = {},
) {
let findAttempts = 0;
for (;;) {
try {
const waitTimeMs = intervalMs * (findAttempts + 1);
console.log(`Attempting to load page at ${path}, waiting ${waitTimeMs}`);
await new Promise(resolve => setTimeout(resolve, waitTimeMs));
await page.goto(`http://localhost:3000${path}`, {
waitUntil: 'networkidle0',
});
const match = await page.evaluate(
textContent =>
Array.from(document.querySelectorAll('*')).some(
el => el.textContent === textContent,
),
text,
);
if (!match) {
throw new Error(`Expected to find text ${text}`);
}
break;
} catch (error) {
console.log(error);
assertError(error);
findAttempts++;
if (findAttempts >= maxFindAttempts) {
throw new Error(
`Failed to load page '${path}', max number of attempts reached`,
);
}
}
}
}
export function print(msg: string) {
return process.stdout.write(`${msg}\n`);
}
@@ -1,5 +0,0 @@
{
"baseUrl": "http://localhost:3001",
"fixturesFolder": false,
"pluginsFile": false
}
@@ -1,12 +0,0 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": [
"error",
{
"assertFunctionNames": ["expect", "cy.contains", "cy.**.should"]
}
]
}
}
@@ -42,21 +42,14 @@
"@testing-library/user-event": "^14.0.0",
"@types/react": "*",
"@types/react-dom": "*",
"cross-env": "^7.0.0",
"cypress": "^10.0.0",
"eslint-plugin-cypress": "^2.10.3",
"start-server-and-test": "^1.10.11"
"cross-env": "^7.0.0"
},
"scripts": {
"start": "backstage-cli package start --config ./app-config.yaml",
"build": "backstage-cli package build --config ./app-config.yaml",
"clean": "backstage-cli package clean",
"test": "backstage-cli package test",
"lint": "backstage-cli package lint",
"test:e2e": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:dev",
"test:e2e:ci": "cross-env PORT=3001 start-server-and-test start http://localhost:3001 cy:run",
"cy:dev": "cypress open",
"cy:run": "cypress run"
"lint": "backstage-cli package lint"
},
"prettier": "@spotify/prettier-config",
"browserslist": {
-35
View File
@@ -75,38 +75,3 @@ pip install mkdocs-techdocs-core
```
Then run `yarn test`.
#### Cypress (Integration and Visual regression) tests
Running cypress tests requires you to run the CLI locally against our example docs.
Run the local version of techdocs-cli against the example docs:
```sh
# From the root of this repository run
# NOTE: This will build the techdocs-cli-embedded-app and copy the output into the cli dist directory
yarn build --scope @techdocs/cli
# Navigate to the example project
cd packages/techdocs-cli/src/example-docs
# Now execute the techdocs-cli serve command
../../bin/techdocs-cli serve
```
In another shell, run the cypress tests:
```sh
# From the root of the project, navigate to the techdocs-cli package
cd packages/techdocs-cli
# Run tests
yarn test:cypress
```
This will launch a cypress app where you can run the two different tests:
- `backstage_serve` - will run against the backstage server
- `mkdocs_serve` - will run test against the mkdocs server
> If its the first time you run Cypress, it will run a "Verifying Cypress can run" step. This step can result in a "Cypress verification timed out" error. If that is the case, let the verification step run and then run the command again and it should succeed.
@@ -18,5 +18,5 @@ const path = require('path');
module.exports = require('@backstage/cli/config/jest').then(baseConfig => ({
...baseConfig,
rootDir: path.resolve(__dirname, 'e2e-tests'),
rootDir: path.resolve(__dirname, 'cli-e2e-tests'),
}));
-30
View File
@@ -1,30 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineConfig } from 'cypress';
import { initPlugin } from '@frsource/cypress-plugin-visual-regression-diff/plugins';
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
initPlugin(on, config);
},
excludeSpecPattern: ['**/__snapshots__/*', '**/__image_snapshots__/*'],
},
viewportWidth: 1920,
viewportHeight: 1080,
includeShadowDom: true,
});
@@ -1,12 +0,0 @@
{
"plugins": ["cypress"],
"extends": ["plugin:cypress/recommended"],
"rules": {
"jest/expect-expect": [
"error",
{
"assertFunctionNames": ["expect", "cy.contains", "cy.document"]
}
]
}
}
@@ -1,58 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
describe('TechDocs Live Preview - Backstage Serve', () => {
it('successfully serves documentation', () => {
cy.visit(`${Cypress.env('backstageBaseUrl')}/docs/default/component/local`);
cy.contains('hello mock docs');
});
it('successfully navigates to sub page of documentation', () => {
cy.contains('SubDocs').click();
cy.contains('Home 2').click();
cy.contains(
'This is an md file in another docs folder using the MkDocs Monorepo Plugin',
);
});
it('successfully renders all Backstage main elements', () => {
cy.contains('header', 'Live preview environment');
cy.get('[data-testid="sidebar-root"]')
.children()
.should('have.length.gt', 0);
});
it('successfully renders all extracted MkDocs main elements', () => {
// as it gets replaced by Backstage header
cy.get('.md-header').should('have.length', 0);
cy.get('.md-main').should('have.length', 1);
cy.contains(
'.md-main',
'This is an md file in another docs folder using the MkDocs Monorepo Plugin',
);
cy.get('.md-sidebar.md-sidebar--primary').should('have.length', 1);
cy.get('.md-sidebar.md-sidebar--primary').should('have.length', 1);
cy.get('.md-footer').should('have.length', 1);
});
it('matchImage - Backstage TechDocs Page', () => {
cy.visit(
`${Cypress.env('backstageBaseUrl')}/docs/default/component/local`,
).then(() => {
cy.document().matchImage();
});
});
});
@@ -1,48 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <reference types="cypress" />
describe('TechDocs Live Preview - MkDocs Serve', () => {
it('successfully serves documentation', () => {
cy.visit(Cypress.env('mkdocsBaseUrl'));
cy.contains('hello mock docs');
});
it('successfully navigates to sub page of documentation', () => {
cy.contains('SubDocs').click();
cy.contains('Home 2').click();
cy.contains(
'This is an md file in another docs folder using the MkDocs Monorepo Plugin',
);
});
it('successfully renders all main elements', () => {
cy.get('.md-header').should('have.length', 1);
cy.get('.md-main').should('have.length', 1);
cy.contains(
'.md-main',
'This is an md file in another docs folder using the MkDocs Monorepo Plugin',
);
cy.get('.md-sidebar.md-sidebar--primary').should('have.length', 1);
cy.get('.md-sidebar.md-sidebar--primary').should('have.length', 1);
cy.get('.md-footer').should('have.length', 1);
});
it('matchImage - MkDocs Page', () => {
cy.visit(Cypress.env('mkdocsBaseUrl')).then(() => {
cy.document().matchImage();
});
});
});
@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
@@ -1,40 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// 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) => { ... })
@@ -1,36 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';
import '@frsource/cypress-plugin-visual-regression-diff/commands';
// Alternatively you can use CommonJS syntax:
// require('./commands')
+2 -5
View File
@@ -27,9 +27,8 @@
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"test:e2e": "backstage-cli test --config e2e-test.config.js",
"test:e2e:ci": "backstage-cli test --config e2e-test.config.js --watchAll=false --ci",
"test:cypress": "cypress open",
"test:e2e": "backstage-cli test --config cli-e2e-test.config.js",
"test:e2e:ci": "backstage-cli test --config cli-e2e-test.config.js --watchAll=false --ci",
"prepack": "./scripts/prepack.sh"
},
"bin": {
@@ -37,14 +36,12 @@
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@frsource/cypress-plugin-visual-regression-diff": "^3.2.8",
"@types/commander": "^2.12.2",
"@types/fs-extra": "^9.0.6",
"@types/http-proxy": "^1.17.4",
"@types/node": "^18.17.8",
"@types/serve-handler": "^6.1.0",
"@types/webpack-env": "^1.15.3",
"cypress": "^10.0.0",
"find-process": "^1.4.5",
"nodemon": "^3.0.1",
"techdocs-cli-embedded-app": "link:../techdocs-cli-embedded-app",
+67
View File
@@ -0,0 +1,67 @@
/*
* Copyright 2023 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { defineConfig } from '@playwright/test';
import { generateProjects } from '@backstage/e2e-test-utils/playwright';
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
timeout: 30_000,
expect: {
timeout: 5_000,
},
// Run your local dev server before starting the tests
webServer: process.env.CI
? []
: [
{
command: 'yarn start',
port: 3000,
reuseExistingServer: true,
timeout: 60_000,
},
// TODO: Before encouraging e2e tests for backend we'll want to provide better utilities for mocking auth
// {
// command: 'yarn start-backend',
// port: 7007,
// reuseExistingServer: true,
// timeout: 60_000,
// },
],
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: [['html', { open: 'never', outputFolder: 'e2e-test-report' }]],
use: {
actionTimeout: 0,
baseURL:
process.env.PLAYWRIGHT_URL ??
(process.env.CI ? 'http://localhost:7007' : 'http://localhost:3000'),
screenshot: 'only-on-failure',
trace: 'on-first-retry',
},
outputDir: 'node_modules/.cache/e2e-test-results',
projects: generateProjects(), // Find all packages with e2e-test folders
});
+1 -1
View File
@@ -36,7 +36,7 @@ async function findLockFiles() {
if (!files.length) {
// List all lock files that are in the root or in an immediate subdirectory
files = ['yarn.lock', 'cypress/yarn.lock', 'microsite/yarn.lock'];
files = ['yarn.lock', 'microsite/yarn.lock'];
}
return files.map(file => ({
+128 -842
View File
File diff suppressed because it is too large Load Diff