diff --git a/packages/cli/config/tsconfig.json b/packages/cli/config/tsconfig.json index abb10087ca..27b8fde4ba 100644 --- a/packages/cli/config/tsconfig.json +++ b/packages/cli/config/tsconfig.json @@ -6,6 +6,8 @@ "noEmit": false, "declarationMap": true, "incremental": true, - "module": "es6" + "module": "es6", + "resolveJsonModule": true, + "esModuleInterop": true } } diff --git a/plugins/lighthouse/README.md b/plugins/lighthouse/README.md index 57a8f316ad..29439b9772 100644 --- a/plugins/lighthouse/README.md +++ b/plugins/lighthouse/README.md @@ -27,7 +27,8 @@ your app's [`plugins.ts`](https://github.com/spotify/backstage/blob/master/packa to enable the plugin: ```js -export { default as LighthousePlugin } from '@backstage/plugin-lighthouse'; +import { default as LighthousePlugin } from '@backstage/plugin-lighthouse'; +export LighthousePlugin; ``` Then, you need to use the `lighthouseApiRef` exported from the plugin to initialize the Rest API in diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index fd9d40ffc7..721f20f802 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -7,7 +7,7 @@ "private": true, "scripts": { "build:watch": "backstage-cli plugin:build --watch", - "build": "backstage-cli build-cache -- backstage-cli plugin:build", + "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", "test": "backstage-cli test" }, diff --git a/plugins/lighthouse/src/api.ts b/plugins/lighthouse/src/api.ts index 07540cc06d..0fb2139301 100644 --- a/plugins/lighthouse/src/api.ts +++ b/plugins/lighthouse/src/api.ts @@ -122,13 +122,18 @@ export class LighthouseRestApi implements LighthouseApi { async getWebsiteList({ limit, offset }: LASListRequest = {}): Promise< WebsiteListResponse > { + const params = new URLSearchParams(); + if (typeof limit === 'number') params.append('limit', limit.toString()); + if (typeof offset === 'number') params.append('offset', offset.toString()); return await this.fetch( - `/v1/websites?limit=${limit}&offset=${offset}`, + `/v1/websites?${params.toString()}`, ); } async getWebsiteForAuditId(auditId: string): Promise { - return await this.fetch(`/v1/audits/${auditId}/website`); + return await this.fetch( + `/v1/audits/${encodeURIComponent(auditId)}/website`, + ); } async triggerAudit(payload: TriggerAuditPayload): Promise { diff --git a/plugins/lighthouse/src/components/AuditList/AuditListTable.test.tsx b/plugins/lighthouse/src/components/AuditList/AuditListTable.test.tsx index 1e01b72ddf..3b82b27645 100644 --- a/plugins/lighthouse/src/components/AuditList/AuditListTable.test.tsx +++ b/plugins/lighthouse/src/components/AuditList/AuditListTable.test.tsx @@ -15,8 +15,6 @@ */ import React from 'react'; -import fs from 'fs'; -import path from 'path'; import { render } from '@testing-library/react'; import { wrapInThemedTestApp } from '@backstage/test-utils'; @@ -24,15 +22,9 @@ import AuditListTable from './AuditListTable'; import { WebsiteListResponse } from '../../api'; import { formatTime } from '../../utils'; -const websiteListResponseJson = fs - .readFileSync( - path.join(__dirname, '../../__fixtures__/website-list-response.json'), - ) - .toString(); +import * as data from '../../__fixtures__/website-list-response.json'; -const websiteListResponse: WebsiteListResponse = JSON.parse( - websiteListResponseJson, -); +const websiteListResponse = data as WebsiteListResponse; describe('AuditListTable', () => { it('renders the link to each website', () => { diff --git a/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx b/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx index 69994253e7..dd9a168e46 100644 --- a/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx +++ b/plugins/lighthouse/src/components/AuditList/AuditListTable.tsx @@ -51,17 +51,21 @@ export const CATEGORY_LABELS: Record = { pwa: 'Progressive Web App', }; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ table: { minWidth: 650, }, status: { textTransform: 'capitalize', }, - link: { padding: '0.75rem 0', display: 'inline-block' }, + link: { + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + display: 'inline-block', + }, statusCell: { whiteSpace: 'nowrap' }, sparklinesCell: { minWidth: 120 }, -}); +})); type SparklinesDataByCategory = Record; function buildSparklinesDataForItem(item: Website): SparklinesDataByCategory { diff --git a/plugins/lighthouse/src/components/AuditList/index.test.tsx b/plugins/lighthouse/src/components/AuditList/index.test.tsx index 5edf963377..2813f554b8 100644 --- a/plugins/lighthouse/src/components/AuditList/index.test.tsx +++ b/plugins/lighthouse/src/components/AuditList/index.test.tsx @@ -27,23 +27,22 @@ jest.mock('react-router-dom', () => { import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import fs from 'fs'; -import path from 'path'; import mockFetch from 'jest-fetch-mock'; import { render, fireEvent } from '@testing-library/react'; import { ApiRegistry, ApiProvider } from '@backstage/core'; import { wrapInThemedTestApp, wrapInTheme } from '@backstage/test-utils'; -import { lighthouseApiRef, LighthouseRestApi } from '../../api'; +import { + lighthouseApiRef, + LighthouseRestApi, + WebsiteListResponse, +} from '../../api'; import AuditList from '.'; -const { useHistory } = require.requireMock('react-router-dom'); +import * as data from '../../__fixtures__/website-list-response.json'; -const websiteListJson = fs - .readFileSync( - path.join(__dirname, '../../__fixtures__/website-list-response.json'), - ) - .toString(); +const { useHistory } = require.requireMock('react-router-dom'); +const websiteListResponse = data as WebsiteListResponse; describe('AuditList', () => { let apis: ApiRegistry; @@ -52,7 +51,7 @@ describe('AuditList', () => { apis = ApiRegistry.from([ [lighthouseApiRef, new LighthouseRestApi('http://lighthouse')], ]); - mockFetch.mockResponse(websiteListJson); + mockFetch.mockResponse(JSON.stringify(websiteListResponse)); }); it('should render the table', async () => { @@ -116,7 +115,7 @@ describe('AuditList', () => { describe('when multiple pages are needed', () => { beforeEach(() => { - const response = JSON.parse(websiteListJson); + const response = { ...websiteListResponse }; response.limit = 5; response.offset = 5; response.total = 7; diff --git a/plugins/lighthouse/src/components/AuditList/index.tsx b/plugins/lighthouse/src/components/AuditList/index.tsx index 234fdb3d5c..4dbbb05295 100644 --- a/plugins/lighthouse/src/components/AuditList/index.tsx +++ b/plugins/lighthouse/src/components/AuditList/index.tsx @@ -50,9 +50,9 @@ const AuditList: FC<{}> = () => { : 1; const lighthouseApi = useApi(lighthouseApiRef); - const { value, loading, error } = useAsync( - async (): Promise => - lighthouseApi.getWebsiteList({ + const { value, loading, error } = useAsync( + async () => + await lighthouseApi.getWebsiteList({ limit: LIMIT, offset: (page - 1) * LIMIT, }), diff --git a/plugins/lighthouse/src/components/AuditView/index.test.tsx b/plugins/lighthouse/src/components/AuditView/index.test.tsx index 977e30d1fb..31034440d6 100644 --- a/plugins/lighthouse/src/components/AuditView/index.test.tsx +++ b/plugins/lighthouse/src/components/AuditView/index.test.tsx @@ -22,8 +22,6 @@ jest.mock('react-router-dom', () => ({ })); import React from 'react'; -import fs from 'fs'; -import path from 'path'; import mockFetch from 'jest-fetch-mock'; import { render } from '@testing-library/react'; import { wrapInThemedTestApp } from '@backstage/test-utils'; @@ -32,29 +30,24 @@ import { ApiRegistry, ApiProvider } from '@backstage/core'; import AuditView from '.'; import { lighthouseApiRef, LighthouseRestApi, Audit, Website } from '../../api'; import { formatTime } from '../../utils'; +import * as data from '../../__fixtures__/website-response.json'; const { useParams }: { useParams: jest.Mock } = require.requireMock( 'react-router-dom', ); - -const websiteResponseJson = fs - .readFileSync( - path.join(__dirname, '../../__fixtures__/website-response.json'), - ) - .toString(); +const websiteResponse = data as Website; describe('AuditView', () => { let apis: ApiRegistry; - let website: Website; let id: string; beforeEach(() => { - mockFetch.mockResponse(websiteResponseJson); + mockFetch.mockResponse(JSON.stringify(websiteResponse)); apis = ApiRegistry.from([ [lighthouseApiRef, new LighthouseRestApi('https://lighthouse')], ]); - website = JSON.parse(websiteResponseJson) as Website; - id = website.audits.find(a => a.status === 'COMPLETED')?.id as string; + id = websiteResponse.audits.find(a => a.status === 'COMPLETED') + ?.id as string; useParams.mockReturnValue({ id }); }); @@ -105,7 +98,7 @@ describe('AuditView', () => { await rendered.findByTestId('audit-sidebar'); - website.audits.forEach(a => { + websiteResponse.audits.forEach(a => { expect( rendered.queryByText(formatTime(a.timeCreated)), ).toBeInTheDocument(); @@ -123,13 +116,15 @@ describe('AuditView', () => { await rendered.findByTestId('audit-sidebar'); - const audit = website.audits.find(a => a.id === id) as Audit; + const audit = websiteResponse.audits.find(a => a.id === id) as Audit; const auditElement = rendered.getByText(formatTime(audit.timeCreated)); expect(auditElement.parentElement?.parentElement?.className).toContain( 'selected', ); - const notSelectedAudit = website.audits.find(a => a.id !== id) as Audit; + const notSelectedAudit = websiteResponse.audits.find( + a => a.id !== id, + ) as Audit; const notSelectedAuditElement = rendered.getByText( formatTime(notSelectedAudit.timeCreated), ); @@ -149,7 +144,7 @@ describe('AuditView', () => { await rendered.findByTestId('audit-sidebar'); - website.audits.forEach(a => { + websiteResponse.audits.forEach(a => { expect( rendered.getByText(formatTime(a.timeCreated)).parentElement ?.parentElement, @@ -188,7 +183,8 @@ describe('AuditView', () => { describe.skip('when a loading audit is accessed', () => { it('shows a loading view', async () => { - id = website.audits.find(a => a.status === 'RUNNING')?.id as string; + id = websiteResponse.audits.find(a => a.status === 'RUNNING') + ?.id as string; useParams.mockReturnValueOnce({ id }); const rendered = render( @@ -207,7 +203,8 @@ describe('AuditView', () => { describe.skip('when a failed audit is accessed', () => { it('shows an error message', async () => { - id = website.audits.find(a => a.status === 'FAILED')?.id as string; + id = websiteResponse.audits.find(a => a.status === 'FAILED') + ?.id as string; useParams.mockReturnValueOnce({ id }); const rendered = render( diff --git a/plugins/lighthouse/src/components/AuditView/index.tsx b/plugins/lighthouse/src/components/AuditView/index.tsx index 09f37c4242..bb766b3997 100644 --- a/plugins/lighthouse/src/components/AuditView/index.tsx +++ b/plugins/lighthouse/src/components/AuditView/index.tsx @@ -106,7 +106,7 @@ const AuditView: FC<{ audit?: Audit }> = ({ audit }: { audit?: Audit }) => {