diff --git a/src/__tests__/CompareResults/SearchInput.test.tsx b/src/__tests__/CompareResults/SearchInput.test.tsx index 7c186000c..942399063 100644 --- a/src/__tests__/CompareResults/SearchInput.test.tsx +++ b/src/__tests__/CompareResults/SearchInput.test.tsx @@ -170,4 +170,41 @@ describe('Search by title/test name', () => { await user.click(clearButton); expect(screen.getByText('spam')).toBeInTheDocument(); }); + + it('should update the url search params properly', async () => { + await setupAndRenderComponent(); + + const searchInput = await screen.findByRole('textbox', { + name: /Search by title/, + }); + + // set delay to null to prevent test time-out due to useFakeTimers + const user = userEvent.setup({ delay: null }); + + // With the mock setup in this test, there are 2 results with different + // properties. In this test we make each of the input show and disappear in + // an alternance, by searching for the values in the different properties. + await user.type(searchInput, 'glvideo'); + await waitFor(() => + expect(screen.queryByText('a11yr')).not.toBeInTheDocument(), + ); + // Make sure that the search params are updated properly. + expect(window.location.search).toContain('&search=glvideo'); + + await user.clear(searchInput); + expect(await screen.findByText('a11yr')).toBeInTheDocument(); + // Clearing the search should remove the search param. + expect(window.location.search).not.toContain('&search'); + + await user.type(searchInput, 'a11yr{Enter}'); + // With enter the update should happen instantly. + expect(window.location.search).toContain('&search=a11yr'); + + const clearButton = screen.getByRole('button', { + name: 'Clear the search input', + }); + await user.click(clearButton); + // Clearing the search through button should also remove the search param. + expect(window.location.search).not.toContain('&search'); + }); }); diff --git a/src/__tests__/CompareResults/__snapshots__/ResultsTable.test.tsx.snap b/src/__tests__/CompareResults/__snapshots__/ResultsTable.test.tsx.snap index 6cbea419d..36807cab4 100644 --- a/src/__tests__/CompareResults/__snapshots__/ResultsTable.test.tsx.snap +++ b/src/__tests__/CompareResults/__snapshots__/ResultsTable.test.tsx.snap @@ -8,7 +8,7 @@ exports[`Results Table Should match snapshot 1`] = ` data-testid="beta-version-compare-results" >

-
+
+
+
+
-
- +
+ + Base + : + + + 596.87 + + ( + firefox + ) +
+
+ chartjs-bubble +
+
+
+
+ 587.15 +
+
+ 593.04 +
+
+ 600.7 +
+
+ 602.04 +
+
+
+ 6.96 + = + 1.17 + % standard deviation +
+
-
-
- - Base - : - - - 596.87 - - ( - firefox - ) -
-
- chartjs-bubble -
-
+
+ + New + : + + + 605.61 + + ( + chrome + ) +
+
+ chartjs-bubble +
+
+
-
- 587.15 -
-
- 593.04 -
-
- 600.7 -
-
- 602.04 -
+ 605.16
- 6.96 - = - 1.17 - % standard deviation + 605.31
-
-
-
-
- - New - : - - - 605.61 - - ( - chrome - ) -
-
- chartjs-bubble -
-
-
- 605.16 -
-
- 605.31 -
-
- 605.61 -
-
- 605.81 -
-
- 607.27 -
+ 605.61
- 0.84 - = - 0.14 - % standard deviation + 605.81 +
+
+ 607.27
-
-
- chartjs-line +
+ 0.84 + = + 0.14 + % standard deviation +
+
+ chartjs-line +
- +
+
+
+ Only one run (consider more runs for greater confidence). + +
+
+ + Base application + + : + firefox + +
+
+ + New application + + : + chrome + +
-
+
+
+
+
-
- +
+ + Base + : + + + 596.87 + + ( + firefox + ) +
+
+ chartjs-bubble +
+
+
+
+ 587.15 +
+
+ 593.04 +
+
+ 600.7 +
+
+ 602.04 +
+
+
+ 6.96 + = + 1.17 + % standard deviation +
+
-
-
- - Base - : - - - 596.87 - - ( - firefox - ) -
-
- chartjs-bubble -
-
+
+ + New + : + + + 605.61 + + ( + chrome + ) +
+
+ chartjs-bubble +
+
+
-
- 587.15 -
-
- 593.04 -
-
- 600.7 -
-
- 602.04 -
+ 605.16
- 6.96 - = - 1.17 - % standard deviation + 605.31
-
-
-
-
- - New - : - - - 605.61 - - ( - chrome - ) -
-
- chartjs-bubble -
-
-
- 605.16 -
-
- 605.31 -
-
- 605.61 -
-
- 605.81 -
-
- 607.27 -
+ 605.61 +
+
+ 605.81
- 0.84 - = - 0.14 - % standard deviation + 607.27
-
-
- chartjs-line +
+ 0.84 + = + 0.14 + % standard deviation +
+
+ chartjs-line +
- +
+
+
+ Only one run (consider more runs for greater confidence). + +
+
+ + Base application + + : + firefox + +
+
+ + New application + + : + chrome + +
-
+
+
+
+
-
- +
+ + Base + : + + + 596.87 + + ( + firefox + ) +
+
+ chartjs-bubble +
+
+
+
+ 587.15 +
+
+ 593.04 +
+
+ 600.7 +
+
+ 602.04 +
+
+
+ 6.96 + = + 1.17 + % standard deviation +
+
-
-
- - Base - : - - - 596.87 - - ( - firefox - ) -
-
- chartjs-bubble -
-
+
+ + New + : + + + 605.61 + + ( + chrome + ) +
+
+ chartjs-bubble +
+
+
-
- 587.15 -
-
- 593.04 -
-
- 600.7 -
-
- 602.04 -
+ 605.16
- 6.96 - = - 1.17 - % standard deviation + 605.31
-
-
-
-
- - New - : - - - 605.61 - - ( - chrome - ) -
-
- chartjs-bubble -
-
-
- 605.16 -
-
- 605.31 -
-
- 605.61 -
-
- 605.81 -
-
- 607.27 -
+ 605.61
- 0.84 - = - 0.14 - % standard deviation + 605.81 +
+
+ 607.27
+
+ 0.84 + = + 0.14 + % standard deviation +
-
- chartjs-line -
+
+
+ chartjs-line
- +
+
+
+ Only one run (consider more runs for greater confidence). + +
+
+ + Base application + + : + firefox + +
+
+ + New application + + : + chrome + +
, + { loader: searchLoader }, ); } @@ -45,19 +47,23 @@ function renderWithCompareResultsURL(component: ReactElement) { route: '/compare-over-time-results/', search: '?baseRepo=try&selectedTimeRange=86400&newRev=coconut&newRepo=try&framework=2', - loader, + loader: overTimeLoader, }); } -// Useful function utilities to get various elements in the page -async function waitForPageReadyAndReturnForm() { - const formName = 'Compare over time form'; - const overTimeTitle = Strings.components.searchDefault.overTime.title; +async function waitForPageReady() { + const overTimeTitle = 'Compare over time'; const compTitle = await screen.findByRole('heading', { name: overTimeTitle, }); expect(compTitle).toBeInTheDocument(); +} + +// Useful function utilities to get various elements in the page +async function waitForPageReadyAndReturnForm() { + await waitForPageReady(); + const formName = 'Compare over time form'; const formElement = await screen.findByRole('form', { name: formName, }); @@ -77,7 +83,7 @@ function getCancelButton() { async function expandOverTimeComponent() { const user = userEvent.setup({ delay: null }); const testExpandedID = 'time-state'; - const headerContent = screen.getByTestId(testExpandedID); + const headerContent = await screen.findByTestId(testExpandedID); await user.click(headerContent); expect(screen.getByTestId(testExpandedID)).toHaveClass( 'compare-card-container--expanded', @@ -100,15 +106,9 @@ describe('Compare Over Time', () => { expect(formElement).toMatchSnapshot('Initial state for the form'); }); - it('has the correct title for the component', async () => { - renderSearchViewComponent(); - const title = 'Compare over time'; - const compTitle = screen.getByRole('heading', { name: title }); - expect(compTitle).toBeInTheDocument(); - }); - it('expands on header click and closes when user clicks base component header', async () => { renderSearchViewComponent(); + await waitForPageReady(); const user = userEvent.setup({ delay: null }); const testExpandedID = 'time-state'; @@ -363,6 +363,7 @@ describe('Compare Over Time', () => { { path: '/', element: , + loader: searchLoader, }, { path: '/compare-over-time-results', element:
}, ]); @@ -519,6 +520,13 @@ describe('Compare Over Time', () => { ).not.toBeInTheDocument(); expect(within(formElement).queryByRole('textbox')).not.toBeInTheDocument(); + //Compare should not be visible + expect( + screen.queryByRole('button', { + name: /Compare/, + }), + ).not.toBeInTheDocument(); + // Click the edit revision const editButton = getEditButton(); await user.click(editButton); @@ -539,6 +547,10 @@ describe('Compare Over Time', () => { screen.getByRole('button', { name: 'Revisions' }), ).toBeInTheDocument(); expect(within(formElement).getByRole('textbox')).toBeInTheDocument(); + const compareButton = await screen.findByRole('button', { + name: /Compare/, + }); + expect(compareButton).toBeInTheDocument(); expect(formElement).toMatchSnapshot('After clicking edit button'); expect(editButton).not.toBeVisible(); @@ -560,12 +572,9 @@ describe('Compare Over Time', () => { }); await user.click(last2daysItem); - const compareButton = await screen.findByRole('button', { - name: /Compare/, - }); - // Press the compare button await user.click(compareButton); + expect(formElement).toMatchSnapshot('After clicking Compare button'); await waitFor(() => { expect(location.href).toContain('selectedTimeRange=172800'); @@ -575,59 +584,9 @@ describe('Compare Over Time', () => { expect(screen.getByText(/alves of coconuts/)).toBeInTheDocument(); expect(screen.getByText(/Last 2 days/)).toBeInTheDocument(); expect(within(formElement).getAllByText('autoland')[0]).toBeInTheDocument(); - }); - - it('should exit edit mode after clicking Compare button', async () => { - renderWithCompareResultsURL( - , - ); - const formElement = await waitForPageReadyAndReturnForm(); - const user = userEvent.setup({ delay: null }); - expect(formElement).toMatchSnapshot( - 'Initial state for the form before exiting edit mode', - ); - - // the readonly should be displayed - const timeReadOnly = document.querySelector( - '#time-search-container--readonly', - ); - expect(timeReadOnly).toBeInTheDocument(); - - //Compare should not be visible - expect( - screen.queryByRole('button', { - name: /Compare/, - }), - ).not.toBeInTheDocument(); - - // Click the edit entry button - const editButton = getEditButton(); - await user.click(editButton); - - const compareButton = await screen.findByRole('button', { - name: /Compare/, - }); - - //Inputs and compare button should be visible - - expect(within(formElement).getByRole('textbox')).toBeInTheDocument(); - expect(compareButton).toBeInTheDocument(); - - //hidden edit button and readOnly - expect(editButton).not.toBeVisible(); - expect(timeReadOnly).not.toBeInTheDocument(); - - // Press the compare button - await user.click(compareButton); - - expect(formElement).toMatchSnapshot('After clicking Compare button'); expect(compareButton).not.toBeVisible(); - - //should see edit button again expect(editButton).toBeVisible(); - - // The inputs should be hidden expect(within(formElement).queryByRole('textbox')).not.toBeInTheDocument(); }); }); diff --git a/src/__tests__/Search/CompareWithBase.test.tsx b/src/__tests__/Search/CompareWithBase.test.tsx index 43e7066e1..d8fd55a08 100644 --- a/src/__tests__/Search/CompareWithBase.test.tsx +++ b/src/__tests__/Search/CompareWithBase.test.tsx @@ -2,8 +2,9 @@ import { ReactElement } from 'react'; import userEvent from '@testing-library/user-event'; -import { loader } from '../../components/CompareResults/loader'; +import { loader as withBaseLoader } from '../../components/CompareResults/loader'; import ResultsView from '../../components/CompareResults/ResultsView'; +import { loader as searchLoader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -42,11 +43,14 @@ function setUpTestData() { ); } -function renderSearchViewComponent() { +async function renderSearchViewComponent() { setUpTestData(); - return renderWithRouter( - , - ); + renderWithRouter(, { + loader: searchLoader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); } function renderWithCompareResultsURL(component: ReactElement) { @@ -55,7 +59,7 @@ function renderWithCompareResultsURL(component: ReactElement) { route: '/compare-results/', search: '?baseRev=coconut&baseRepo=try&newRev=spam&newRepo=mozilla-central&framework=2', - loader, + loader: withBaseLoader, }); } @@ -110,15 +114,8 @@ describe('Compare With Base', () => { expect(formElement).toMatchSnapshot('Initial state for the form'); }); - it('has the correct title for the component', async () => { - renderSearchViewComponent(); - const title = 'Compare with a base'; - const compTitle = screen.getByRole('heading', { name: title }); - expect(compTitle).toBeInTheDocument(); - }); - it('expands when user clicks on title header', async () => { - renderSearchViewComponent(); + await renderSearchViewComponent(); const user = userEvent.setup({ delay: null }); const testExpandedTimeID = 'time-state'; @@ -166,7 +163,7 @@ describe('Compare With Base', () => { }); it('selects and displays new framework when clicked', async () => { - renderSearchViewComponent(); + await renderSearchViewComponent(); const formElement = await waitForPageReadyAndReturnForm(); const user = userEvent.setup({ delay: null }); expect(within(formElement).getByText(/talos/i)).toBeInTheDocument(); @@ -190,7 +187,7 @@ describe('Compare With Base', () => { }); it('should remove the checked revision once X button is clicked', async () => { - renderSearchViewComponent(); + await renderSearchViewComponent(); // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); @@ -280,11 +277,20 @@ describe('Compare With Base', () => { // Do the same operation with the components for the "new" revisions const newSearchContainer = document.querySelector('#new-search-container'); expect(newSearchContainer).toHaveClass('hide-container'); + expect( + screen.queryByRole('button', { + name: /Compare/, + }), + ).not.toBeInTheDocument(); // Click the edit revision for new revisions await user.click(getEditButton()); expect(newSearchContainer).toHaveClass('show-container'); + const compareButton = screen.getByRole('button', { + name: /Compare/, + }); + expect(compareButton).toBeInTheDocument(); // Remove the new revision by clicking the X button await user.click(getRemoveRevisionButton(1, 'coconut')); @@ -292,6 +298,18 @@ describe('Compare With Base', () => { expect( within(newSelectedRevision).queryByText(/no arms left/), ).not.toBeInTheDocument(); + + // Press the compare button + await user.click(compareButton); + + expect(formElement).toMatchSnapshot('After clicking Compare button'); + + // The form should be back at its initial state. + expect(compareButton).not.toBeVisible(); + expect(editButton).toBeVisible(); + expect(baseSearchContainer).toHaveClass('hide-container'); + + await waitFor(() => expect(location.href).not.toContain('newRev=spam')); }); it('updates the framework and url when a new one is selected', async () => { @@ -324,56 +342,6 @@ describe('Compare With Base', () => { expect(awsy).toBeInTheDocument(); }); - it('should exit edit mode after clicking Compare button', async () => { - renderWithCompareResultsURL( - , - ); - const formElement = await waitForPageReadyAndReturnForm(); - const user = userEvent.setup({ delay: null }); - expect(formElement).toMatchSnapshot( - 'Initial state for the form before exiting edit mode', - ); - const baseSearchContainer = document.querySelector( - '#base-search-container', - ); - - //Input and compare button should not be visible - expect(baseSearchContainer).toHaveClass('hide-container'); - expect( - screen.queryByRole('button', { - name: /Compare/, - }), - ).not.toBeInTheDocument(); - - // Click the edit entry button - const editButton = getEditButton(); - await user.click(editButton); - - const compareButton = await screen.findByRole('button', { - name: /Compare/, - }); - - //Input and compare button should be visible - expect(baseSearchContainer).toHaveClass('show-container'); - expect(compareButton).toBeInTheDocument(); - - expect(editButton).not.toBeVisible(); - - // Press the compare button - await user.click(compareButton); - - expect(formElement).toMatchSnapshot('After clicking Compare button'); - - // The compare button should not be visible - expect(compareButton).not.toBeVisible(); - - //should see edit button again - expect(editButton).toBeVisible(); - - // The search container should be hidden - expect(baseSearchContainer).toHaveClass('hide-container'); - }); - it('should move back to the previously selected base and new revisions when Cancel is clicked', async () => { renderWithCompareResultsURL( , diff --git a/src/__tests__/Search/SearchContainer.test.tsx b/src/__tests__/Search/SearchContainer.test.tsx index c6a14337f..d0446c509 100644 --- a/src/__tests__/Search/SearchContainer.test.tsx +++ b/src/__tests__/Search/SearchContainer.test.tsx @@ -1,8 +1,13 @@ import React from 'react'; +import { loader } from '../../components/Search/loader'; import SearchContainer from '../../components/Search/SearchContainer'; import getTestData from '../utils/fixtures'; -import { renderWithRouter, FetchMockSandbox } from '../utils/test-utils'; +import { + renderWithRouter, + screen, + FetchMockSandbox, +} from '../utils/test-utils'; function setupTestData() { const { testData } = getTestData(); @@ -17,12 +22,13 @@ function setupTestData() { function renderComponent() { setupTestData(); const ref: React.RefObject = React.createRef(); - renderWithRouter(); + renderWithRouter(, { loader }); } describe('Search Containter', () => { it('should match snapshot', async () => { renderComponent(); + await screen.findByText('Compare with a base or over time'); expect(document.body).toMatchSnapshot(); }); diff --git a/src/__tests__/Search/SearchResultsList.test.tsx b/src/__tests__/Search/SearchResultsList.test.tsx index 6e60304ff..54b90ca0d 100644 --- a/src/__tests__/Search/SearchResultsList.test.tsx +++ b/src/__tests__/Search/SearchResultsList.test.tsx @@ -1,5 +1,6 @@ import userEvent from '@testing-library/user-event'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -10,8 +11,13 @@ import { FetchMockSandbox, } from '../utils/test-utils'; -function renderComponent() { - renderWithRouter(); +async function renderComponent() { + renderWithRouter(, { + loader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); } describe('SearchResultsList', () => { @@ -29,7 +35,7 @@ describe('SearchResultsList', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; await user.click(searchInput); @@ -41,7 +47,7 @@ describe('SearchResultsList', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; await user.click(searchInput); @@ -56,7 +62,7 @@ describe('SearchResultsList', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; @@ -82,7 +88,7 @@ describe('SearchResultsList', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; await user.click(searchInput); @@ -108,7 +114,7 @@ describe('SearchResultsList', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[1]; await user.click(searchInput); diff --git a/src/__tests__/Search/SearchView.test.tsx b/src/__tests__/Search/SearchView.test.tsx index a37b01573..0f2aca795 100644 --- a/src/__tests__/Search/SearchView.test.tsx +++ b/src/__tests__/Search/SearchView.test.tsx @@ -1,6 +1,8 @@ import userEvent from '@testing-library/user-event'; import { RouterProvider, createBrowserRouter } from 'react-router-dom'; +import { repoMap } from '../../common/constants'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -10,6 +12,7 @@ import { render, renderWithRouter, waitFor, + within, FetchMockSandbox, } from '../utils/test-utils'; @@ -25,7 +28,22 @@ function setupTestData() { return { results: testData.filter((item) => item.author === author) }; }, ) - .get('begin:https://treeherder.mozilla.org/api/project/try/push/', { + .get( + 'glob:https://treeherder.mozilla.org/api/project/*/push/?revision=*', + (urlAsString) => { + const url = new URL(urlAsString); + const revision = url.searchParams.get('revision'); + const repository = url.pathname.split('/')[3]; + return { + results: testData.filter( + (item) => + item.revision === revision && + repoMap[item.repository_id] === repository, + ), + }; + }, + ) + .get('glob:https://treeherder.mozilla.org/api/project/*/push/*', { results: testData, }); } @@ -50,16 +68,38 @@ async function expandWithBaseComponent() { ); } -function renderComponent() { +async function getOverTimeForm() { + const formName = 'Compare over time form'; + const formElement = await screen.findByRole('form', { + name: formName, + }); + return formElement; +} + +async function getWithBaseForm() { + const formName = 'Compare with base form'; + const formElement = await screen.findByRole('form', { + name: formName, + }); + return formElement; +} + +async function renderComponent( + options?: Partial<{ route: string; search: string }>, +) { setupTestData(); - return renderWithRouter( - , - ); + renderWithRouter(, { + loader, + ...options, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); } describe('Search View', () => { it('renders correctly when there are no results', async () => { - renderComponent(); + await renderComponent(); // We have to account for the dropdown position // Shift focus to base search @@ -68,14 +108,14 @@ describe('Search View', () => { }); it('renders skip to search link correctly', async () => { - renderComponent(); + await renderComponent(); expect( screen.getByRole('link', { name: /skip to search/i }), ).toBeInTheDocument(); }); it('renders a skip link that sends the focus directly to search container', async () => { - renderComponent(); + await renderComponent(); // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); @@ -87,7 +127,7 @@ describe('Search View', () => { describe('Search Container', () => { it('renders compare with base', async () => { - renderComponent(); + await renderComponent(); const compTitle = await screen.findByRole('heading', { name: baseTitle, @@ -106,7 +146,7 @@ describe('Search Container', () => { describe('Base and OverTime Search', () => { it('renders repository dropdown in closed condition in both Base and OverTime components', async () => { - renderComponent(); + await renderComponent(); // 'try' is selected by default and dropdown is not visible expect(screen.getAllByText(/try/i)[0]).toBeInTheDocument(); expect(screen.queryByText(/mozilla-central/i)).not.toBeInTheDocument(); @@ -135,7 +175,7 @@ describe('Base and OverTime Search', () => { }); it('renders framework dropdown in closed condition', async () => { - renderComponent(); + await renderComponent(); // 'talos' is selected by default and dropdown is not visible expect(screen.getAllByText(/talos/i)[0]).toBeInTheDocument(); expect(screen.queryByText(/build_metrics/i)).not.toBeInTheDocument(); @@ -145,7 +185,7 @@ describe('Base and OverTime Search', () => { it('should hide search results when clicking outside of search input', async () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // Click inside the input box to show search results. const searchInput = screen.getAllByRole('textbox')[0]; @@ -163,7 +203,7 @@ describe('Base and OverTime Search', () => { it('Should hide the search results when Escape key is pressed', async () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // Click inside the input box to show search results. @@ -182,7 +222,7 @@ describe('Base and OverTime Search', () => { it('Should not call fetch if search value is not a hash or email', async () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); const searchInput = screen.getAllByRole('textbox')[0]; @@ -230,7 +270,7 @@ describe('Base and OverTime Search', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.click(searchInput); @@ -284,7 +324,7 @@ describe('Base and OverTime Search', () => { it('Should clear search results if the search value is cleared', async () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'terrygilliam@python.com'); @@ -307,7 +347,7 @@ describe('Base and OverTime Search', () => { it('should not hide search results when clicking search results', async () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; @@ -332,11 +372,11 @@ describe('Base and OverTime Search', () => { (global.fetch as FetchMockSandbox).mock('*', { throws: new Error() }); // This test will output an error to the console. Let's silence it. jest.spyOn(console, 'error').mockImplementation(() => {}); - renderComponent(); + await renderComponent(); act(() => void jest.runAllTimers()); expect(global.fetch).toHaveBeenCalledWith( - 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true', + 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true&count=30', undefined, ); const errorElements = await screen.findAllByText('An error has occurred'); @@ -359,6 +399,7 @@ describe('Base and OverTime Search', () => { { path: '/', element: , + loader, }, { path: '/compare-results', element:
}, ]); @@ -420,3 +461,135 @@ describe('Base and OverTime Search', () => { ); }); }); + +describe('With search parameters', () => { + it('both search components are populated as expected when revision and repository are specified', async () => { + await renderComponent({ search: '?newRev=spamspam&newRepo=try' }); + const withBaseForm = await getWithBaseForm(); + expect( + within(withBaseForm).getByRole('link', { name: /spamspam/ }), + ).toBeInTheDocument(); + expect( + within(withBaseForm).getByRole('button', { name: 'Base' }), + ).toHaveTextContent('try'); + expect( + within(withBaseForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('try'); + expect( + within(withBaseForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('talos'); + expect(withBaseForm).toMatchSnapshot('with base form'); + + await expandOverTimeComponent(); + const overtimeForm = await getOverTimeForm(); + expect( + within(overtimeForm).getByRole('link', { name: /spamspam/ }), + ).toBeInTheDocument(); + expect( + within(overtimeForm).getByRole('button', { name: /Base repository/ }), + ).toHaveTextContent('try'); + expect( + within(overtimeForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('try'); + expect( + within(overtimeForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('talos'); + expect(overtimeForm).toMatchSnapshot('over time form'); + }); + + it('both search components are populated as expected when revision, repository and framework are specified', async () => { + await renderComponent({ + search: + '?newRev=spamspamspamandeggs&newRepo=autoland&frameworkName=browsertime', + }); + const withBaseForm = await getWithBaseForm(); + expect( + within(withBaseForm).getByRole('link', { name: /spamspamspam/ }), // Note that the revision is truncated + ).toBeInTheDocument(); + expect( + within(withBaseForm).getByRole('button', { name: 'Base' }), + ).toHaveTextContent('autoland'); + expect( + within(withBaseForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('autoland'); + expect( + within(withBaseForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('browsertime'); + expect(withBaseForm).toMatchSnapshot('with base form'); + + await expandOverTimeComponent(); + const overtimeForm = await getOverTimeForm(); + expect( + within(overtimeForm).getByRole('link', { name: /spamspamspam/ }), + ).toBeInTheDocument(); + expect( + within(overtimeForm).getByRole('button', { name: /Base repository/ }), + ).toHaveTextContent('autoland'); + expect( + within(overtimeForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('autoland'); + expect( + within(overtimeForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('browsertime'); + expect(overtimeForm).toMatchSnapshot('over time form'); + }); + + it('displays the default values if some values are bogus', async () => { + jest.spyOn(console, 'warn').mockImplementation(); + await renderComponent({ + search: '?newRev=spamspamspamandeggs&newRepo=foo', + }); + + expect(console.warn).toHaveBeenCalledWith( + "The repository foo wasn't found in our list.", + ); + const withBaseForm = await getWithBaseForm(); + expect(withBaseForm).toMatchSnapshot('with base form'); + + await expandOverTimeComponent(); + const overtimeForm = await getOverTimeForm(); + expect(overtimeForm).toMatchSnapshot('over time form'); + }); + + it('displays the default value for framework if the framework value is bogus', async () => { + jest.spyOn(console, 'warn').mockImplementation(); + + await renderComponent({ + search: '?newRev=spamspam&newRepo=try&frameworkName=foo', + }); + expect(console.warn).toHaveBeenCalledWith( + "The framework entry for foo wasn't found, defaulting to talos.", + ); + + const withBaseForm = await getWithBaseForm(); + expect( + within(withBaseForm).getByRole('link', { name: /spamspam/ }), + ).toBeInTheDocument(); + expect( + within(withBaseForm).getByRole('button', { name: 'Base' }), + ).toHaveTextContent('try'); + expect( + within(withBaseForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('try'); + expect( + within(withBaseForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('talos'); + expect(withBaseForm).toMatchSnapshot('with base form'); + + await expandOverTimeComponent(); + const overtimeForm = await getOverTimeForm(); + expect( + within(overtimeForm).getByRole('link', { name: /spamspam/ }), + ).toBeInTheDocument(); + expect( + within(overtimeForm).getByRole('button', { name: /Base repository/ }), + ).toHaveTextContent('try'); + expect( + within(overtimeForm).getByRole('button', { name: 'Revisions' }), + ).toHaveTextContent('try'); + expect( + within(overtimeForm).getByRole('button', { name: /Framework/ }), + ).toHaveTextContent('talos'); + expect(overtimeForm).toMatchSnapshot('over time form'); + }); +}); diff --git a/src/__tests__/Search/SelectedRevision.test.tsx b/src/__tests__/Search/SelectedRevision.test.tsx index f9b170fd5..ea5ed7dab 100644 --- a/src/__tests__/Search/SelectedRevision.test.tsx +++ b/src/__tests__/Search/SelectedRevision.test.tsx @@ -1,5 +1,6 @@ import userEvent from '@testing-library/user-event'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -10,8 +11,13 @@ import { FetchMockSandbox, } from '../utils/test-utils'; -function renderComponent() { - renderWithRouter(); +async function renderComponent() { + renderWithRouter(, { + loader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); } describe('SelectedRevision', () => { @@ -30,7 +36,7 @@ describe('SelectedRevision', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); // focus input to show results const searchInput = screen.getAllByRole('textbox')[0]; await user.click(searchInput); @@ -61,7 +67,7 @@ describe('SelectedRevision', () => { it('should show warning icon on selected try revision when try base is compared with a non try repository', async () => { const user = userEvent.setup({ delay: null }); - renderComponent(); + await renderComponent(); const baseDropdown = screen.getByRole('button', { name: 'Base' }); expect(baseDropdown).toHaveTextContent('try'); @@ -83,7 +89,7 @@ describe('SelectedRevision', () => { }); await user.click(mozRepoItem); const alertIcon = await screen.findByRole('img', { - name: 'Comparing “try” repository to any repository aside from “try” is not recommended.', + name: 'Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.', }); expect(alertIcon).toBeInTheDocument(); }); diff --git a/src/__tests__/Search/__snapshots__/CompareOverTime.test.tsx.snap b/src/__tests__/Search/__snapshots__/CompareOverTime.test.tsx.snap index 22f0ed13a..a2251969a 100644 --- a/src/__tests__/Search/__snapshots__/CompareOverTime.test.tsx.snap +++ b/src/__tests__/Search/__snapshots__/CompareOverTime.test.tsx.snap @@ -796,7 +796,7 @@ exports[`Compare Over Time selects and displays new time range when clicked 1`] `; -exports[`Compare Over Time should exit edit mode after clicking Compare button: After clicking Compare button 1`] = ` +exports[`Compare Over Time should have an edit mode in Results View: After clicking edit button 1`] = `
+
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
@@ -1038,7 +1211,7 @@ exports[`Compare Over Time should exit edit mode after clicking Compare button:
+
`; -exports[`Compare Over Time should exit edit mode after clicking Compare button: Initial state for the form before exiting edit mode 1`] = ` +exports[`Compare Over Time should have an edit mode in Results View: Initial state for the form 1`] = `
`; -exports[`Compare Over Time should have an edit mode in Results View: After clicking edit button 1`] = ` +exports[`Compare Over Time should remove the checked revision once X button is clicked 1`] = ` - - - - + />
-
-
-
    + +
diff --git a/src/__tests__/Search/__snapshots__/CompareWithBase.test.tsx.snap b/src/__tests__/Search/__snapshots__/CompareWithBase.test.tsx.snap index 6444f2635..7c7c1a640 100644 --- a/src/__tests__/Search/__snapshots__/CompareWithBase.test.tsx.snap +++ b/src/__tests__/Search/__snapshots__/CompareWithBase.test.tsx.snap @@ -198,7 +198,7 @@ exports[`Compare With Base renders correctly when there are no results: Initial class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -479,7 +479,7 @@ exports[`Compare With Base renders correctly when there are no results: Initial class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -752,7 +752,7 @@ exports[`Compare With Base selects and displays new framework when clicked 1`] = `; -exports[`Compare With Base should exit edit mode after clicking Compare button: After clicking Compare button 1`] = ` +exports[`Compare With Base should have an edit mode in Results View: After clicking Compare button 1`] = ` + + + + + +
@@ -959,7 +975,7 @@ exports[`Compare With Base should exit edit mode after clicking Compare button: class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -1215,144 +1231,7 @@ exports[`Compare With Base should exit edit mode after clicking Compare button: >
    -
  • - - -
    -
    - try -
    -
    - - - - Comparing “try” repository to any repository aside from “try” is not recommended. - - -
    -
    -
    -
    - - - - coconut - - -
    -
    - - - - johncleese@python.com -
    -
    - - 03/29/51 00:00 -
    -
    -
    - - you've got no arms left! - -
    - - -
    -
  • -
+ />
@@ -1392,628 +1271,6 @@ exports[`Compare With Base should exit edit mode after clicking Compare button: `; -exports[`Compare With Base should exit edit mode after clicking Compare button: Initial state for the form before exiting edit mode 1`] = ` -
-
- -
-
- -
- -
-
-
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
    -
  • - - -
    -
    - try -
    -
    - - - - Comparing “try” repository to any repository aside from “try” is not recommended. - - -
    -
    -
    -
    - - - - coconut - - -
    -
    - - - - johncleese@python.com -
    -
    - - 03/29/51 00:00 -
    -
    -
    - - you've got no arms left! - -
    - - -
    -
  • -
-
-
-
-
- -
- -
-
-
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
    -
  • - - -
    -
    - try -
    -
    - - - - Comparing “try” repository to any repository aside from “try” is not recommended. - - -
    -
    -
    -
    - - - - coconut - - -
    -
    - - - - johncleese@python.com -
    -
    - - 03/29/51 00:00 -
    -
    -
    - - you've got no arms left! - -
    - - -
    -
  • -
-
-
-
- -
-`; - exports[`Compare With Base should have an edit mode in Results View: After clicking edit button 1`] = `
- Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -2502,7 +1759,7 @@ exports[`Compare With Base should have an edit mode in Results View: After click class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -2854,7 +2111,7 @@ exports[`Compare With Base should have an edit mode in Results View: Initial sta class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -3135,7 +2392,7 @@ exports[`Compare With Base should have an edit mode in Results View: Initial sta class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -3637,7 +2894,7 @@ exports[`Compare With Base should have an edit mode in Results View: after remov class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.
@@ -4014,7 +3271,7 @@ exports[`Compare With Base should have an edit mode in Results View: after remov class="warning-icon" > - Comparing “try” repository to any repository aside from “try” is not recommended. + Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons. @@ -4328,7 +3585,7 @@ exports[`Compare With Base should remove the checked revision once X button is c Skip to search








`; + +exports[`With search parameters both search components are populated as expected when revision and repository are specified: over time form 1`] = ` + +
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + try +
    +
    +
    +
    + + + + spamspam + + +
    +
    + + + + terrygilliam@python.com +
    +
    + + 07/29/87 00:00 +
    +
    +
    + + What, ridden on a horse? + +
    + + +
    +
  • +
+
+
+
+ + +`; + +exports[`With search parameters both search components are populated as expected when revision and repository are specified: with base form 1`] = ` +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + try +
    +
    +
    +
    + + + + spamspam + + +
    +
    + + + + terrygilliam@python.com +
    +
    + + 07/29/87 00:00 +
    +
    +
    + + What, ridden on a horse? + +
    + + +
    +
  • +
+
+
+
+ +
+`; + +exports[`With search parameters both search components are populated as expected when revision, repository and framework are specified: over time form 1`] = ` +
+
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + autoland +
    +
    +
    +
    + + + + spamspamspam + + +
    +
    + + + + michaelpalin@python.com +
    +
    + + 07/04/20 00:00 +
    +
    +
    + + You've got two empty 'alves of coconuts and you're bangin' 'em togetha + +
    + + +
    +
  • +
+
+
+
+ +
+`; + +exports[`With search parameters both search components are populated as expected when revision, repository and framework are specified: with base form 1`] = ` +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + autoland +
    +
    +
    +
    + + + + spamspamspam + + +
    +
    + + + + michaelpalin@python.com +
    +
    + + 07/04/20 00:00 +
    +
    +
    + + You've got two empty 'alves of coconuts and you're bangin' 'em togetha + +
    + + +
    +
  • +
+
+
+
+ +
+`; + +exports[`With search parameters displays the default value for framework if the framework value is bogus: over time form 1`] = ` +
+
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + try +
    +
    +
    +
    + + + + spamspam + + +
    +
    + + + + terrygilliam@python.com +
    +
    + + 07/29/87 00:00 +
    +
    +
    + + What, ridden on a horse? + +
    + + +
    +
  • +
+
+
+
+ +
+`; + +exports[`With search parameters displays the default value for framework if the framework value is bogus: with base form 1`] = ` +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
  • + + +
    +
    + try +
    +
    +
    +
    + + + + spamspam + + +
    +
    + + + + terrygilliam@python.com +
    +
    + + 07/29/87 00:00 +
    +
    +
    + + What, ridden on a horse? + +
    + + +
    +
  • +
+
+
+
+ +
+`; + +exports[`With search parameters displays the default values if some values are bogus: over time form 1`] = ` +
+
+ +
+
+ +
+
+
+ +
+
+ +
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+ +
+`; + +exports[`With search parameters displays the default values if some values are bogus: with base form 1`] = ` +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+
+
    +
+
+
+ +
+`; diff --git a/src/__tests__/Search/fetchRecentRevisions.test.tsx b/src/__tests__/Search/fetchRecentRevisions.test.tsx index 5ee17aa5b..3c3e44fdd 100644 --- a/src/__tests__/Search/fetchRecentRevisions.test.tsx +++ b/src/__tests__/Search/fetchRecentRevisions.test.tsx @@ -1,5 +1,6 @@ import userEvent from '@testing-library/user-event'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -9,6 +10,15 @@ import { FetchMockSandbox, } from '../utils/test-utils'; +async function renderSearchViewComponent() { + renderWithRouter(, { + loader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); +} + describe('Search View/fetchRecentRevisions', () => { it('should fetch and display recent results when repository is selected', async () => { const { testData } = getTestData(); @@ -22,8 +32,7 @@ describe('Search View/fetchRecentRevisions', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); - + await renderSearchViewComponent(); const baseRepoSelect = screen.getAllByRole('button', { name: 'Base' })[0]; expect(baseRepoSelect).toHaveTextContent('try'); await user.click(baseRepoSelect); @@ -45,7 +54,7 @@ describe('Search View/fetchRecentRevisions', () => { await screen.findAllByText("you've got no arms left!"); expect(global.fetch).toHaveBeenCalledWith( - 'https://treeherder.mozilla.org/api/project/autoland/push/?hide_reviewbot_pushes=true', + 'https://treeherder.mozilla.org/api/project/autoland/push/?hide_reviewbot_pushes=true&count=30', undefined, ); }); @@ -58,7 +67,7 @@ describe('Search View/fetchRecentRevisions', () => { }, ); - renderWithRouter(); + await renderSearchViewComponent(); const errorMessages = await screen.findAllByText('No results found'); expect(errorMessages).toHaveLength(3); @@ -67,7 +76,7 @@ describe('Search View/fetchRecentRevisions', () => { expect(inputs[0]).toBeInvalid(); expect(inputs[1]).toBeInvalid(); expect(global.fetch).toHaveBeenCalledWith( - 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true', + 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true&count=30', undefined, ); }); @@ -84,7 +93,7 @@ describe('Search View/fetchRecentRevisions', () => { // This test will output an error to the console. Let's silence it. jest.spyOn(console, 'error').mockImplementation(() => {}); - renderWithRouter(); + await renderSearchViewComponent(); const errorMessages = await screen.findAllByText(errorMessage); expect(errorMessages).toHaveLength(3); @@ -93,7 +102,7 @@ describe('Search View/fetchRecentRevisions', () => { expect(inputs[0]).toBeInvalid(); expect(inputs[1]).toBeInvalid(); expect(global.fetch).toHaveBeenCalledWith( - 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true', + 'https://treeherder.mozilla.org/api/project/try/push/?hide_reviewbot_pushes=true&count=30', undefined, ); expect(console.error).toHaveBeenCalledWith( diff --git a/src/__tests__/Search/fetchRevisionByID.test.tsx b/src/__tests__/Search/fetchRevisionByID.test.tsx index 7b8a22604..bfe317d0c 100644 --- a/src/__tests__/Search/fetchRevisionByID.test.tsx +++ b/src/__tests__/Search/fetchRevisionByID.test.tsx @@ -1,5 +1,6 @@ import userEvent from '@testing-library/user-event'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -10,6 +11,15 @@ import { FetchMockSandbox, } from '../utils/test-utils'; +async function renderSearchViewComponent() { + renderWithRouter(, { + loader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); +} + describe('Search View/fetchRevisionByID', () => { it('should fetch revisions by ID if searchValue is a 12 or 40 character hash', async () => { const { testData } = getTestData(); @@ -24,7 +34,7 @@ describe('Search View/fetchRevisionByID', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'abcdef123456'); @@ -68,7 +78,7 @@ describe('Search View/fetchRevisionByID', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'abcdef1234567890abcdef1234567890abcdef12'); @@ -101,7 +111,7 @@ describe('Search View/fetchRevisionByID', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'abcdef1234567890abcdef1234567890abcdef12'); @@ -136,7 +146,7 @@ describe('Search View/fetchRevisionByID', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'abcdef123456'); diff --git a/src/__tests__/Search/fetchRevisionsByAuthor.test.tsx b/src/__tests__/Search/fetchRevisionsByAuthor.test.tsx index a1a600158..6bcb638ec 100644 --- a/src/__tests__/Search/fetchRevisionsByAuthor.test.tsx +++ b/src/__tests__/Search/fetchRevisionsByAuthor.test.tsx @@ -1,5 +1,6 @@ import userEvent from '@testing-library/user-event'; +import { loader } from '../../components/Search/loader'; import SearchView from '../../components/Search/SearchView'; import { Strings } from '../../resources/Strings'; import getTestData from '../utils/fixtures'; @@ -10,6 +11,15 @@ import { FetchMockSandbox, } from '../utils/test-utils'; +async function renderSearchViewComponent() { + renderWithRouter(, { + loader, + }); + const title = 'Compare with a base'; + const compTitle = await screen.findByRole('heading', { name: title }); + expect(compTitle).toBeInTheDocument(); +} + describe('SearchView/fetchRevisionsByAuthor', () => { it('should fetch revisions by author if searchValue is an email address', async () => { const { testData } = getTestData(); @@ -23,7 +33,7 @@ describe('SearchView/fetchRevisionsByAuthor', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); expect(screen.getAllByText('try')[0]).toBeInTheDocument(); const searchInput = screen.getAllByRole('textbox')[0]; @@ -51,7 +61,7 @@ describe('SearchView/fetchRevisionsByAuthor', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'ericidle@python.com'); @@ -84,7 +94,7 @@ describe('SearchView/fetchRevisionsByAuthor', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'grahamchapman@python.com'); @@ -119,7 +129,7 @@ describe('SearchView/fetchRevisionsByAuthor', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + await renderSearchViewComponent(); const searchInput = screen.getAllByRole('textbox')[0]; await user.type(searchInput, 'grahamchapman@python.com'); diff --git a/src/__tests__/Snackbar.test.tsx b/src/__tests__/Snackbar.test.tsx index af3cbedf2..737c3474f 100644 --- a/src/__tests__/Snackbar.test.tsx +++ b/src/__tests__/Snackbar.test.tsx @@ -1,6 +1,7 @@ import userEvent from '@testing-library/user-event'; import App from '../components/App'; +import { loader } from '../components/Search/loader'; import SearchView from '../components/Search/SearchView'; import { Strings } from '../resources/Strings'; import getTestData from './utils/fixtures'; @@ -70,10 +71,12 @@ describe('Snackbar', () => { // set delay to null to prevent test time-out due to useFakeTimers const user = userEvent.setup({ delay: null }); - renderWithRouter(); + renderWithRouter(, { + loader, + }); // focus input to show results - const searchInput = screen.getAllByRole('textbox')[1]; + const searchInput = (await screen.findAllByRole('textbox'))[1]; await user.click(searchInput); await user.click(await screen.findByTestId('checkbox-0')); diff --git a/src/__tests__/__snapshots__/App.test.tsx.snap b/src/__tests__/__snapshots__/App.test.tsx.snap index 60d95dac3..e842e5af7 100644 --- a/src/__tests__/__snapshots__/App.test.tsx.snap +++ b/src/__tests__/__snapshots__/App.test.tsx.snap @@ -60,7 +60,7 @@ exports[`App CompareResults or CompareOverTime loader Should render an error pag class="fcndgf6" >
+ + + + + + + + + + + + + + + + diff --git a/src/assets/clock-light.svg b/src/assets/clock-light.svg new file mode 100644 index 000000000..217778cfb --- /dev/null +++ b/src/assets/clock-light.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/assets/header-background.svg b/src/assets/header-background.svg new file mode 100644 index 000000000..aa357f29f --- /dev/null +++ b/src/assets/header-background.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/overlapping-circles-dark.svg b/src/assets/overlapping-circles-dark.svg new file mode 100644 index 000000000..99caab3cb --- /dev/null +++ b/src/assets/overlapping-circles-dark.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/assets/overlapping-circles-light.svg b/src/assets/overlapping-circles-light.svg new file mode 100644 index 000000000..e1cf74808 --- /dev/null +++ b/src/assets/overlapping-circles-light.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/components/App.tsx b/src/components/App.tsx index 4f755f790..0dc18bb30 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -24,6 +24,7 @@ import { loader as compareSubtestsLoader } from './CompareResults/subtestsLoader import { loader as compareSubtestsOverTimeLoader } from './CompareResults/subtestsOverTimeLoader'; import SubtestsOverTimeResultsView from './CompareResults/SubtestsResults/SubtestsOverTimeResultsView'; import SubtestsResultsView from './CompareResults/SubtestsResults/SubtestsResultsView'; +import { loader as homeLoader } from './Search/loader'; import SearchView from './Search/SearchView'; import { PageError } from './Shared/PageError'; import SnackbarCloseButton from './Shared/SnackbarCloseButton'; @@ -68,7 +69,9 @@ export const router = createBrowserRouter( <> } + errorElement={} /> state.theme.mode); const [searchParams, setSearchParams] = useSearchParams(); - const [searchTerm, setSearchTerm] = useState(''); + // This is our custom hook that updates the search params without a rerender. + const [rawSearchParams, updateRawSearchParams] = useRawSearchParams(); + const initialSearchTerm = rawSearchParams.get('search') ?? ''; + const [searchTerm, setSearchTerm] = useState(initialSearchTerm); const [frameworkIdVal, setFrameworkIdVal] = useState(frameworkId); const themeColor100 = @@ -61,13 +65,26 @@ function ResultsMain() { setSearchParams(searchParams); }; + const onSearchTermChange = (newSearchTerm: string) => { + setSearchTerm(newSearchTerm); + if (newSearchTerm) { + rawSearchParams.set('search', newSearchTerm); + } else { + rawSearchParams.delete('search'); + } + updateRawSearchParams(rawSearchParams); + }; + return (
Results
- + diff --git a/src/components/CompareResults/RevisionRowExpandable.tsx b/src/components/CompareResults/RevisionRowExpandable.tsx index b8fb87bff..07b5c10b9 100644 --- a/src/components/CompareResults/RevisionRowExpandable.tsx +++ b/src/components/CompareResults/RevisionRowExpandable.tsx @@ -10,14 +10,6 @@ import Distribution from './Distribution'; const strings = Strings.components.expandableRow; const { singleRun } = strings; -function shouldDisplayGraphDistribution( - baseRuns: Array, - newRuns: Array, -): boolean { - if (baseRuns.length > 1 || newRuns.length > 1) return true; - return false; -} - function RevisionRowExpandable(props: RevisionRowExpandableProps) { const { result } = props; const { @@ -25,18 +17,10 @@ function RevisionRowExpandable(props: RevisionRowExpandableProps) { delta_percentage: deltaPercent, delta_value: delta, confidence_text: confidenceText, - base_runs: baseRuns, - new_runs: newRuns, - base_runs_replicates: baseRunsReplicates, - new_runs_replicates: newRunsReplicates, new_is_better: newIsBetter, base_app: baseApplication, new_app: newApplication, } = result; - const shouldDisplayGraph = shouldDisplayGraphDistribution( - baseRunsReplicates.length ? baseRunsReplicates : baseRuns, - newRunsReplicates.length ? newRunsReplicates : newRuns, - ); const themeMode = useAppSelector((state) => state.theme.mode); const themeColor200 = @@ -70,32 +54,26 @@ function RevisionRowExpandable(props: RevisionRowExpandableProps) {
{platform}
- {shouldDisplayGraph && ( -
-
- {' '} -
- -
- )}
- {' '} + +
+ +
+ +
+
+
{singleRun}
+ {baseApplication && ( +
+ Base application: {baseApplication}{' '} +
+ )} + {newApplication && ( +
+ New application: {newApplication}{' '} +
+ )}
- {!shouldDisplayGraph && ( -
-
{singleRun}
- {baseApplication && ( -
- Base application: {baseApplication}{' '} -
- )} - {newApplication && ( -
- New application: {newApplication}{' '} -
- )} -
- )}
Mean Difference: {deltaPercent}%{' '} {newIsBetter ? 'better' : 'worse'} ({delta}) diff --git a/src/components/CompareResults/SearchInput.tsx b/src/components/CompareResults/SearchInput.tsx index 537543c6b..6fb654d5a 100644 --- a/src/components/CompareResults/SearchInput.tsx +++ b/src/components/CompareResults/SearchInput.tsx @@ -8,10 +8,11 @@ import { Strings } from '../../resources/Strings'; import { simpleDebounce } from '../../utils/simple-debounce'; interface SearchInputProps { + defaultValue?: string; onChange: (searchTerm: string) => unknown; } -function SearchInput({ onChange }: SearchInputProps) { - const [searchTerm, setSearchTerm] = useState(''); +function SearchInput({ defaultValue, onChange }: SearchInputProps) { + const [searchTerm, setSearchTerm] = useState(defaultValue ?? ''); // By using useCallback, we ensure that we always have the same instance of // onDebouncedChange, so that when calling `clear` the timeout will always be diff --git a/src/components/CompareResults/SubtestsResults/SubtestsResultsMain.tsx b/src/components/CompareResults/SubtestsResults/SubtestsResultsMain.tsx index a6f575e69..c940f097b 100644 --- a/src/components/CompareResults/SubtestsResults/SubtestsResultsMain.tsx +++ b/src/components/CompareResults/SubtestsResults/SubtestsResultsMain.tsx @@ -7,6 +7,7 @@ import { style } from 'typestyle'; import { subtestsView, subtestsOverTimeView } from '../../../common/constants'; import { useAppSelector } from '../../../hooks/app'; +import useRawSearchParams from '../../../hooks/useRawSearchParams'; import { Colors, Spacing } from '../../../styles'; import type { SubtestsRevisionsHeader } from '../../../types/state'; import DownloadButton from '.././DownloadButton'; @@ -28,7 +29,11 @@ function SubtestsResultsMain({ view }: SubtestsResultsMainProps) { | OvertimeLoaderReturnValue; const themeMode = useAppSelector((state) => state.theme.mode); - const [searchTerm, setSearchTerm] = useState(''); + + // This is our custom hook that updates the search params without a rerender. + const [rawSearchParams, updateRawSearchParams] = useRawSearchParams(); + const initialSearchTerm = rawSearchParams.get('search') ?? ''; + const [searchTerm, setSearchTerm] = useState(initialSearchTerm); const subtestsHeader: SubtestsRevisionsHeader = { suite: results[0].suite, @@ -56,6 +61,16 @@ function SubtestsResultsMain({ view }: SubtestsResultsMainProps) { }), }; + const onSearchTermChange = (newSearchTerm: string) => { + setSearchTerm(newSearchTerm); + if (newSearchTerm) { + rawSearchParams.set('search', newSearchTerm); + } else { + rawSearchParams.delete('search'); + } + updateRawSearchParams(rawSearchParams); + }; + return (
@@ -63,7 +78,10 @@ function SubtestsResultsMain({ view }: SubtestsResultsMainProps) { - + diff --git a/src/components/CompareResults/loader.ts b/src/components/CompareResults/loader.ts index 126529ad0..69b1dc2c2 100644 --- a/src/components/CompareResults/loader.ts +++ b/src/components/CompareResults/loader.ts @@ -207,8 +207,6 @@ export async function loader({ request }: { request: Request }) { framework: frameworkId, }); - // For each of these requests, we get a list of 1 item because we request one - // specific hash. // TODO what happens if there's no result? const baseRevInfoPromise = memoizedFetchRevisionForRepository({ repository: baseRepo, diff --git a/src/components/CompareResults/subtestsOverTimeLoader.tsx b/src/components/CompareResults/subtestsOverTimeLoader.tsx index 60ed0a274..16c3a70cd 100644 --- a/src/components/CompareResults/subtestsOverTimeLoader.tsx +++ b/src/components/CompareResults/subtestsOverTimeLoader.tsx @@ -142,7 +142,7 @@ export async function loader({ request }: { request: Request }) { const baseParentSignatureFromUrl = url.searchParams.get( 'baseParentSignature', ); - const newParentSignatureFromUrl = url.searchParams.get('baseParentSignature'); + const newParentSignatureFromUrl = url.searchParams.get('newParentSignature'); const { baseRepo, diff --git a/src/components/Search/SearchContainer.tsx b/src/components/Search/SearchContainer.tsx index b7ae8886d..0d45a4611 100644 --- a/src/components/Search/SearchContainer.tsx +++ b/src/components/Search/SearchContainer.tsx @@ -1,13 +1,15 @@ import React, { useState } from 'react'; import Typography from '@mui/material/Typography'; +import { useLoaderData } from 'react-router-dom'; import { useAppSelector } from '../../hooks/app'; import { Strings } from '../../resources/Strings'; import { SearchContainerStyles } from '../../styles'; -import type { Framework, TimeRange } from '../../types/types'; +import type { TimeRange } from '../../types/types'; import CompareOverTime from './CompareOverTime'; import CompareWithBase from './CompareWithBase'; +import type { LoaderReturnValue } from './loader'; const strings = Strings.components.searchDefault; @@ -15,6 +17,8 @@ function SearchContainer(props: SearchViewProps) { const themeMode = useAppSelector((state) => state.theme.mode); const styles = SearchContainerStyles(themeMode, /* isHome */ true); const [isBaseSearchExpanded, setIsBaseSearchExpanded] = useState(true); + const { newRevInfo, newRepo, frameworkId } = + useLoaderData() as LoaderReturnValue; return (
setIsBaseSearchExpanded(true)} - baseRepo='try' - newRepo='try' + baseRepo={newRepo} + newRepo={newRepo} /> setIsBaseSearchExpanded(false)} - frameworkIdVal={1 as Framework['id']} + frameworkIdVal={frameworkId} intervalValue={86400 as TimeRange['value']} - baseRepo='try' - newRepo='try' + baseRepo={newRepo} + newRepo={newRepo} />
); diff --git a/src/components/Search/loader.ts b/src/components/Search/loader.ts new file mode 100644 index 000000000..9883cf724 --- /dev/null +++ b/src/components/Search/loader.ts @@ -0,0 +1,117 @@ +import { repoMap, frameworks } from '../../common/constants'; +import { memoizedFetchRevisionForRepository } from '../../logic/treeherder'; +import { Changeset, Repository } from '../../types/state'; +import { Framework } from '../../types/types'; + +const DEFAULT_VALUES = { + newRev: null, + newRevInfo: null, + newRepo: 'try' as Repository['name'], + frameworkId: 1 as Framework['id'], + frameworkName: 'talos' as Framework['name'], +}; + +// This function checks and sanitizes the input values, then returns values that +// we can then use in the rest of the application. +function checkValues({ + newRev, + newRepo, + frameworkName, +}: { + newRev: string | null; + newRepo: Repository['name'] | null; + frameworkName: Framework['name'] | null; +}): null | { + newRev: string; + newRepo: Repository['name']; + frameworkId: Framework['id']; + frameworkName: Framework['name']; +} { + if (newRev === null || newRepo === null) { + return null; + } + + const validRepoValues = Object.values(repoMap); + if (!validRepoValues.includes(newRepo)) { + console.warn(`The repository ${newRepo} wasn't found in our list.`); + return null; + } + + if (frameworkName === null) { + frameworkName = DEFAULT_VALUES.frameworkName; + } + + let frameworkId = frameworks.find( + (entry) => entry.name === frameworkName, + )?.id; + + if (frameworkId === undefined) { + // Default to talos if the entry wasn't found. + console.warn( + `The framework entry for ${frameworkName} wasn't found, defaulting to talos.`, + ); + frameworkId = DEFAULT_VALUES.frameworkId; + frameworkName = DEFAULT_VALUES.frameworkName; + } + + return { + newRev, + newRepo, + frameworkId, + frameworkName, + }; +} + +// This function is responsible for fetching the data from the URL. It's called +// by React Router DOM when the compare-results path is requested. +// It uses the URL parameters as inputs, and returns all the fetched data to the +// React components through React Router's useLoaderData hook. +export async function loader({ request }: { request: Request }) { + const url = new URL(request.url); + const newRevFromUrl = url.searchParams.get('newRev'); + const newRepoFromUrl = url.searchParams.get('newRepo') as + | Repository['name'] + | null; + const frameworkFromUrl = url.searchParams.get('frameworkName') as + | Framework['name'] + | null; + + const checkedValues = checkValues({ + newRev: newRevFromUrl, + newRepo: newRepoFromUrl, + frameworkName: frameworkFromUrl, + }); + if (!checkedValues) { + return DEFAULT_VALUES; + } + + const { newRev, newRepo, frameworkId, frameworkName } = checkedValues; + + const newRevInfo = await memoizedFetchRevisionForRepository({ + repository: newRepo, + hash: newRev, + }); + + if (!newRevInfo) { + // The search returned no result. + return DEFAULT_VALUES; + } + + return { + newRev, + newRevInfo, + newRepo, + frameworkId, + frameworkName, + }; +} + +// Be explicit with the returned type to control it better than if we were +// inferring it. +export type LoaderReturnValue = { + newRev: string; + newRevInfo: Changeset; + newRepo: Repository['name']; + frameworkId: Framework['id']; + frameworkName: Framework['name']; +}; diff --git a/src/hooks/useRawSearchParams.ts b/src/hooks/useRawSearchParams.ts new file mode 100644 index 000000000..23d742919 --- /dev/null +++ b/src/hooks/useRawSearchParams.ts @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; + +type UpdateRawSearchParamsOptions = { + method?: 'replace' | 'push'; +}; + +type UpdateRawSearchParams = ( + newSearchParams: URLSearchParams, + options?: UpdateRawSearchParamsOptions, +) => void; + +type RawSearchParams = [URLSearchParams, UpdateRawSearchParams]; + +/** + * This hook provides an `updateSearchParams` function that can update the + * search parameters _without_ creating a re-render on the router level. + */ +const useRawSearchParams = (): RawSearchParams => { + // Memoize the current search parameters to avoid unnecessary recalculations + const searchParams = useMemo(() => { + return new URLSearchParams(window.location.search); + }, [window.location.search]); + + // This function updates the search params without causing a re-render + // and provides flexibility to use either pushState or replaceState. + const updateSearchParams: UpdateRawSearchParams = ( + newSearchParams, + options = { method: 'replace' }, + ) => { + const newUrl = `?${newSearchParams.toString()}`; + + // Depending on the method, use either pushState or replaceState + // to update the URL without causing a component re-render. + if (options.method === 'push') { + window.history.pushState(null, '', newUrl); + } else { + window.history.replaceState(null, '', newUrl); + } + }; + + return [searchParams, updateSearchParams]; +}; + +export default useRawSearchParams; diff --git a/src/logic/treeherder.ts b/src/logic/treeherder.ts index 241c126dd..35b94fdd1 100644 --- a/src/logic/treeherder.ts +++ b/src/logic/treeherder.ts @@ -189,7 +189,7 @@ function computeUrlFromSearchTermAndRepository({ return baseUrl + '?revision=' + hash; } - return baseUrl + '?hide_reviewbot_pushes=true'; + return baseUrl + '?hide_reviewbot_pushes=true&count=30'; } // This fetches the recent revisions on a specific repository, optionally diff --git a/src/resources/Strings.tsx b/src/resources/Strings.tsx index 8b4b9b76c..90100a10c 100644 --- a/src/resources/Strings.tsx +++ b/src/resources/Strings.tsx @@ -45,15 +45,12 @@ export const Strings = { base: { title: 'Compare with a base', tagline: 'Analyze differences between specific revisions.', - img: 'https://user-images.githubusercontent.com/88336547/233237125-1534220b-c343-421a-9133-ce8f151cb979.png', - imgDark: - 'https://user-images.githubusercontent.com/88336547/233250674-004d071a-7c23-40f4-b348-0687a3fef6e3.png', compareBtn: 'Compare', editText: 'Edit entry', collapsed: { warnings: { comparison: - 'Comparing “try” repository to any repository aside from “try” is not recommended.', + 'Production (e.g. mozilla-central, autoland), and try branches have different performance characteristics due to build differences that can often result in misleading comparisons.', }, errors: { notEnoughRevisions: 'Please select at least one base revision.', @@ -76,9 +73,6 @@ export const Strings = { title: 'Compare over time', tagline: 'Analyze differences between revisions within a specific time range.', - img: 'https://user-images.githubusercontent.com/88336547/233250659-4012551b-e07a-44ce-accb-242e29d31914.png', - imgDark: - 'https://user-images.githubusercontent.com/88336547/233250642-7fd7c217-e72b-4375-9078-7ed2f99cb0f7.png', collapsed: { errors: { notEnoughRevisions: 'Please select at least one revision.', diff --git a/src/styles/CompareCards.ts b/src/styles/CompareCards.ts index 1ae6f37f0..ea1e791b1 100644 --- a/src/styles/CompareCards.ts +++ b/src/styles/CompareCards.ts @@ -1,6 +1,9 @@ import { stylesheet } from 'typestyle'; -import { Strings } from '../resources/Strings'; +import clockDark from '../assets/clock-dark.svg'; +import clockLight from '../assets/clock-light.svg'; +import overlappingCirclesDark from '../assets/overlapping-circles-dark.svg'; +import overlappingCirclesLight from '../assets/overlapping-circles-light.svg'; import { FontsRaw, Spacing, @@ -9,8 +12,6 @@ import { CardsLightRaw, } from '../styles'; -const strings = Strings.components.searchDefault; - const textLightMode = { color: `${Colors.PrimaryText} !important`, }; @@ -24,6 +25,12 @@ const repoDropdownWidth = 200; export const CompareCardsStyles = (mode: string) => { const isTrueLight = mode == 'light' ? true : false; + const overlappingCircles = isTrueLight + ? overlappingCirclesLight + : overlappingCirclesDark; + + const clock = isTrueLight ? clockLight : clockDark; + const compareCardsCSS = stylesheet({ container: { ...(isTrueLight ? CardsLightRaw : CardsDarkRaw), @@ -51,14 +58,10 @@ export const CompareCardsStyles = (mode: string) => { : Colors.Background300Dark, $nest: { '&.compare-card-img--time': { - backgroundImage: `url(${ - isTrueLight ? strings.overTime.img : strings.overTime.imgDark - })`, + backgroundImage: `url(${clock.toString()})`, }, '&.compare-card-img--base': { - backgroundImage: `url(${ - isTrueLight ? strings.base.img : strings.base.imgDark - })`, + backgroundImage: `url(${overlappingCircles.toString()})`, }, }, }, diff --git a/src/styles/Header.ts b/src/styles/Header.ts index 6b14988c0..62cef6cd9 100644 --- a/src/styles/Header.ts +++ b/src/styles/Header.ts @@ -1,23 +1,23 @@ import { stylesheet } from 'typestyle'; -import { Strings } from '../resources/Strings'; +import headerBGImage from '../assets/header-background.svg'; import { Colors } from './Colors'; import { Spacing } from './Spacing'; -const strings = Strings.components.header; - export const HeaderStyles = (mode: string, isHome: boolean) => { const isTrueLight = mode == 'light'; const lightBg = isHome ? Colors.Background200 : '#ffffff'; const darkBg = isHome ? Colors.Background200Dark : Colors.Background100Dark; + const headerImage = isHome ? headerBGImage : 'none'; + const styles = stylesheet({ container: { padding: 0, width: '100%', minHeight: isHome ? '357px' : '130px', backgroundColor: isTrueLight ? lightBg : darkBg, - backgroundImage: isHome ? strings.bgLink : 'none', + backgroundImage: `url(${headerImage.toString()})`, backgroundPosition: 'center', backgroundRepeat: 'no-repeat', backgroundPositionY: 'top',