Skip to content

Commit 9b5d731

Browse files
Merge pull request #46 from zalando/upgrade/backstage-1.39.0
2 parents f31499d + 6a361d3 commit 9b5d731

File tree

25 files changed

+12664
-5661
lines changed

25 files changed

+12664
-5661
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dev/**/*
2+
!dev/index.tsx

.github/workflows/npm-publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
fetch-depth: 0
2727
- uses: actions/setup-node@v4
2828
with:
29-
node-version: 18
29+
node-version: 20
3030
registry-url: https://registry.npmjs.org/
3131
always-auth: true
3232
env:

.github/workflows/verify.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
node-version: [18.x]
13+
node-version: [20.x]
1414

1515
steps:
1616
- uses: actions/checkout@v4

backstage.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "1.8.3"
2+
"version": "1.39.0"
33
}

dev/index.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
// eslint-disable-next-line import/no-extraneous-dependencies
2-
import React from 'react';
3-
// eslint-disable-next-line import/no-extraneous-dependencies
41
import { createDevApp } from '@backstage/dev-utils';
5-
import { APILinterPlugin } from '../src/plugin';
6-
import { APILinter } from '../src';
2+
import { APILinter, APILinterPlugin } from '../src';
73

84
createDevApp()
95
.registerPlugin(APILinterPlugin)

package.json

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,78 +13,82 @@
1313
"role": "frontend-plugin"
1414
},
1515
"dependencies": {
16-
"@backstage/core-components": "^0.12.0",
17-
"@backstage/core-plugin-api": "^1.0.5",
18-
"@backstage/theme": "^0.2.13",
16+
"@backstage/core-components": "^0.17.2",
17+
"@backstage/core-plugin-api": "^1.10.7",
18+
"@backstage/theme": "^0.6.6",
1919
"@material-ui/core": "^4.12.2",
2020
"@material-ui/icons": "^4.9.1",
2121
"@material-ui/lab": "4.0.0-alpha.57",
2222
"ace-builds": "^1.4.14",
2323
"brace": "^0.11.1",
24-
"react": "^17.0.2",
2524
"react-ace": "^9.5.0",
26-
"react-dom": "^17.0.2",
2725
"react-router-dom": "^6.4.5",
2826
"react-use": "^17.3.2",
29-
"swagger-ui-react": "^4.12.0"
27+
"swagger-ui-react": "^5.30.1"
3028
},
3129
"peerDependencies": {
32-
"@backstage/core-components": "^0.12.0",
33-
"@backstage/core-plugin-api": "^1.0.5",
34-
"@backstage/theme": "^0.2.13",
30+
"@backstage/core-components": "^0.17.2",
31+
"@backstage/core-plugin-api": "^1.10.7",
32+
"@backstage/theme": "^0.6.6",
3533
"@material-ui/core": "^4.12.2",
3634
"@material-ui/icons": "^4.9.1",
37-
"@material-ui/lab": "4.0.0-alpha.57"
35+
"@material-ui/lab": "4.0.0-alpha.57",
36+
"react": "^18",
37+
"react-dom": "^18"
3838
},
3939
"devDependencies": {
40-
"@backstage/cli": "^0.21.1",
41-
"@backstage/core-app-api": "^1.2.0",
42-
"@backstage/core-components": "^0.12.0",
43-
"@backstage/core-plugin-api": "^1.0.5",
44-
"@backstage/dev-utils": "^1.0.8",
45-
"@backstage/test-utils": "^1.2.2",
46-
"@backstage/theme": "^0.2.13",
40+
"@backstage/cli": "^0.32.1",
41+
"@backstage/core-app-api": "^1.17.0",
42+
"@backstage/core-components": "^0.17.2",
43+
"@backstage/core-plugin-api": "^1.10.7",
44+
"@backstage/dev-utils": "^1.1.10",
45+
"@backstage/test-utils": "^1.7.8",
46+
"@backstage/theme": "^0.6.6",
4747
"@commitlint/cli": "^19.5.0",
4848
"@commitlint/config-conventional": "^19.5.0",
4949
"@material-ui/core": "^4.12.2",
5050
"@material-ui/icons": "^4.9.1",
5151
"@material-ui/lab": "4.0.0-alpha.57",
5252
"@semantic-release/github": "^10.3.5",
5353
"@semantic-release/npm": "^12.0.1",
54-
"@spotify/prettier-config": "^15.0.0",
54+
"@spotify/prettier-config": "*",
55+
"@testing-library/dom": "^10.4.1",
5556
"@testing-library/jest-dom": "^6.9.1",
56-
"@testing-library/react": "^12.1.3",
57-
"@testing-library/user-event": "^13.1.8",
58-
"@types/jest": "^29",
57+
"@testing-library/react": "^16.3.0",
58+
"@testing-library/user-event": "^14.6.1",
59+
"@types/jest": "^30",
5960
"@types/node": "*",
60-
"@types/react": "^17",
61-
"@types/react-dom": "^17",
62-
"@types/swagger-ui-react": "^4.1.1",
61+
"@types/react": "^18",
62+
"@types/react-dom": "^18",
63+
"@types/swagger-ui-react": "^5.18.0",
6364
"commitizen": "^4.3.1",
6465
"concurrently": "^9.2.1",
6566
"cross-fetch": "^3.0.6",
6667
"cz-conventional-changelog": "^3.3.0",
67-
"eslint-plugin-jest": "^27",
68+
"eslint": "^8.57.0",
69+
"eslint-plugin-jest": "^29.0.1",
6870
"husky": "^8.0.1",
6971
"msw": "^0.35.0",
7072
"prettier": "^3.6.2",
7173
"pretty-quick": "^4.2.2",
72-
"semantic-release": "19.0.2"
74+
"react": "^18",
75+
"react-dom": "^18",
76+
"semantic-release": "^25.0.1"
7377
},
7478
"scripts": {
75-
"start:backend": "yarn --cwd dev start",
7679
"postinstall": "husky install",
7780
"build": "backstage-cli package build",
7881
"start": "backstage-cli package start",
7982
"lint": "backstage-cli package lint",
8083
"test": "backstage-cli package test",
81-
"dev": "concurrently \"yarn start\" \"yarn start:backend\"",
82-
"tsc": "npx tsc",
83-
"test:ci": "backstage-cli test --no-watch --silent --forceExit --detectOpenHandles --no-cache --coverage=false --runInBand",
8484
"diff": "backstage-cli plugin:diff",
8585
"prepack": "backstage-cli package prepack",
8686
"postpack": "backstage-cli package postpack",
8787
"clean": "backstage-cli package clean",
88+
"start:backend": "yarn --cwd dev start",
89+
"dev": "concurrently \"yarn start\" \"yarn start:backend\"",
90+
"tsc": "npx tsc",
91+
"test:ci": "backstage-cli package test --ci --no-watchman --no-watch --silent --no-cache --coverage=false",
8892
"format:all": "prettier \"**/*.{ts,tsx,js,jsx,json,md}\" --write",
8993
"format:check": "prettier \"**/*.{ts,tsx,js,jsx,json,md}\" --check",
9094
"format:staged": "pretty-quick --staged",
@@ -120,9 +124,10 @@
120124
}
121125
},
122126
"prettier": "@spotify/prettier-config",
127+
"engines": {
128+
"node": ">=20.0.0 || >=22.0.0"
129+
},
123130
"resolutions": {
124-
"@types/react": "^17.0.0",
125-
"react": "^17.0.2",
126-
"react-dom": "^17.0.2"
131+
"eslint": "8.57.0"
127132
}
128133
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { APILinter } from '.';
2+
import { ThemeProvider } from '@material-ui/core';
3+
import { themes } from '@backstage/theme';
4+
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
5+
import userEvent from '@testing-library/user-event';
6+
import { fireEvent } from '@testing-library/react';
7+
import { zallyApiRef } from '../../api';
8+
import { mockZallyApi, mockZallyApiEmpty } from './__tests__/mocks';
9+
import schema from './__tests__/schemaMock.json';
10+
import '@testing-library/jest-dom';
11+
12+
const stringSchema = encodeURIComponent(JSON.stringify(schema));
13+
14+
function renderApp(withEmpty = false) {
15+
const mockApi = withEmpty ? mockZallyApiEmpty : mockZallyApi;
16+
return renderInTestApp(
17+
<TestApiProvider apis={[[zallyApiRef, mockApi]]}>
18+
<ThemeProvider theme={themes.light}>
19+
<APILinter />
20+
</ThemeProvider>
21+
</TestApiProvider>,
22+
);
23+
}
24+
25+
describe('APILinter', () => {
26+
let originalSessionStorage: Storage;
27+
28+
beforeAll(() => {
29+
originalSessionStorage = window.sessionStorage;
30+
Object.defineProperty(window, 'sessionStorage', {
31+
writable: true,
32+
value: {
33+
getItem: jest.fn().mockName('getItem'),
34+
setItem: jest.fn().mockName('setItem'),
35+
},
36+
});
37+
});
38+
39+
beforeEach(() => {
40+
(sessionStorage.getItem as jest.Mock).mockClear();
41+
(sessionStorage.setItem as jest.Mock).mockClear();
42+
});
43+
44+
afterAll(() => {
45+
Object.defineProperty(window, 'sessionStorage', {
46+
writable: true,
47+
value: originalSessionStorage,
48+
});
49+
});
50+
51+
it('should render rules', async () => {
52+
const { getByText, findAllByTestId } = await renderApp();
53+
fireEvent.click(getByText(/view the rules/i));
54+
const ruleCards = await findAllByTestId('rule');
55+
expect(ruleCards.length).toBeGreaterThan(0);
56+
});
57+
58+
it('should render URL dialog and display violation', async () => {
59+
const { getByText, getByTestId, findByTestId, findAllByTestId } =
60+
await renderApp();
61+
62+
fireEvent.click(getByText(/import url/i));
63+
const urlInput = getByText(/Enter the url to import from/i);
64+
await userEvent.type(urlInput, 'https://www.rawapi.com.br/api.json');
65+
fireEvent.click(getByTestId(/url-validate/i));
66+
const violations = await findAllByTestId(/violation/i);
67+
expect(violations.length).toBe(1);
68+
expect(await findByTestId('must')).toBeInTheDocument();
69+
});
70+
71+
it('should render Schema and display violations', async () => {
72+
const { getByText, getByTestId, findAllByTestId } = await renderApp();
73+
const schemaInput = getByText(/Paste a swagger schema here/i);
74+
expect(schemaInput).toBeInTheDocument();
75+
await userEvent.type(schemaInput, stringSchema);
76+
fireEvent.click(getByTestId(/schema-validate/i));
77+
const violations = await findAllByTestId(/violation/i);
78+
expect(violations.length).toBe(1);
79+
});
80+
});
81+
82+
describe('APILinter - No violations / Error', () => {
83+
it('should display perfect badge when no violations are found', async () => {
84+
const { getByTestId, getByText, findByTestId } = await renderApp(true);
85+
fireEvent.click(getByText(/import url/i));
86+
const urlInput = getByText(/Enter the url to import from/i);
87+
await userEvent.type(urlInput, 'https://www.perfectapi.com.br/api.json');
88+
fireEvent.click(getByTestId(/url-validate/i));
89+
expect(await findByTestId(/perfect/i)).toBeInTheDocument();
90+
});
91+
92+
it('should not submit when user types invalid url', async () => {
93+
const { getByText, getByTestId } = await renderApp(true);
94+
fireEvent.click(getByText(/import url/i));
95+
const urlInput = getByText(/Enter the url to import from/i);
96+
await userEvent.type(urlInput, 'invalid-url');
97+
fireEvent.click(getByTestId(/url-validate/i));
98+
expect(getByText(/enter the URL to import from/i)).toBeInTheDocument();
99+
});
100+
101+
it('should render Schema and display correct badge when no violations are found', async () => {
102+
const { getByText, findByTestId } = await renderApp(true);
103+
const schemaInput = getByText(/Paste a swagger schema here/i);
104+
await userEvent.type(schemaInput, stringSchema);
105+
fireEvent.click(getByText(/Validate/i));
106+
expect(await findByTestId('perfect')).toBeInTheDocument();
107+
});
108+
});

src/components/APILinter/APILinter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { Schemas } from '../Schemas';
33
import { useApi } from '@backstage/core-plugin-api';
44
import { zallyApiRef } from '../../api';

0 commit comments

Comments
 (0)