From de0bdb3a091fa110064c4b9096091da8df878603 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 10 Apr 2025 13:26:04 -0700 Subject: [PATCH 1/3] add tests for datePicker components and move around files for better folder structure --- .../codecov/datePicker/datePicker.spec.tsx | 149 ++++++++++++++++++ .../codecov/{ => datePicker}/datePicker.tsx | 8 +- .../codecov/datePicker/dateSelector.spec.tsx | 59 +++++++ .../codecov/{ => datePicker}/dateSelector.tsx | 0 static/app/components/codecov/utils.spec.tsx | 16 ++ static/app/components/codecov/utils.tsx | 6 +- static/app/views/codecov/tests/tests.tsx | 2 +- 7 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 static/app/components/codecov/datePicker/datePicker.spec.tsx rename static/app/components/codecov/{ => datePicker}/datePicker.tsx (85%) create mode 100644 static/app/components/codecov/datePicker/dateSelector.spec.tsx rename static/app/components/codecov/{ => datePicker}/dateSelector.tsx (100%) create mode 100644 static/app/components/codecov/utils.spec.tsx diff --git a/static/app/components/codecov/datePicker/datePicker.spec.tsx b/static/app/components/codecov/datePicker/datePicker.spec.tsx new file mode 100644 index 00000000000000..ae6b7cd624ef21 --- /dev/null +++ b/static/app/components/codecov/datePicker/datePicker.spec.tsx @@ -0,0 +1,149 @@ +import {initializeOrg} from 'sentry-test/initializeOrg'; +import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary'; + +import {initializeUrlState} from 'sentry/actionCreators/pageFilters'; +import {DatePicker} from 'sentry/components/codecov/datePicker/datePicker'; +import OrganizationStore from 'sentry/stores/organizationStore'; +import PageFiltersStore from 'sentry/stores/pageFiltersStore'; + +const {organization, router} = initializeOrg({ + router: { + location: { + query: {}, + pathname: '/codecov/tests', + }, + params: {}, + }, +}); + +describe('DatePicker', function () { + beforeEach(() => { + PageFiltersStore.init(); + OrganizationStore.init(); + + OrganizationStore.onUpdate(organization, {replace: true}); + PageFiltersStore.onInitializeUrlState( + { + projects: [], + environments: [], + datetime: { + period: '7d', + start: null, + end: null, + utc: false, + }, + }, + new Set(['datetime']) + ); + }); + + it('can change period', async function () { + render(, {router, organization}); + + await userEvent.click(screen.getByRole('button', {name: '7D', expanded: false})); + await userEvent.click(screen.getByRole('option', {name: 'Last 30 days'})); + + // Confirm selection changed visible text and query params + expect( + screen.getByRole('button', {name: '30D', expanded: false}) + ).toBeInTheDocument(); + expect(router.push).toHaveBeenCalledWith( + expect.objectContaining({query: {statsPeriod: '30d'}}) + ); + expect(PageFiltersStore.getState()).toEqual({ + isReady: true, + shouldPersist: true, + desyncedFilters: new Set(), + pinnedFilters: new Set(['datetime']), + selection: { + datetime: { + period: '30d', + end: null, + start: null, + utc: false, + }, + environments: [], + projects: [], + }, + }); + }); + + it('adjusts period if invalid', async function () { + PageFiltersStore.reset(); + PageFiltersStore.onInitializeUrlState( + { + projects: [], + environments: [], + datetime: { + period: '123d', + start: null, + end: null, + utc: false, + }, + }, + new Set(['datetime']) + ); + + render(, {router, organization}); + + // Confirm selection changed to default Codecov period + const button = await screen.findByRole('button', {name: '24H', expanded: false}); + expect(button).toBeInTheDocument(); + expect(router.push).toHaveBeenCalledWith( + expect.objectContaining({query: {statsPeriod: '24h'}}) + ); + expect(PageFiltersStore.getState()).toEqual({ + isReady: true, + shouldPersist: true, + desyncedFilters: new Set(), + pinnedFilters: new Set(['datetime']), + selection: { + datetime: { + period: '24h', + end: null, + start: null, + utc: false, + }, + environments: [], + projects: [], + }, + }); + }); + + it('displays a desynced state message', async function () { + const {organization: desyncOrganization, router: desyncRouter} = initializeOrg({ + router: { + location: { + query: {statsPeriod: '7d'}, + pathname: '/codecov/test', + }, + params: {}, + }, + }); + + PageFiltersStore.reset(); + initializeUrlState({ + memberProjects: [], + nonMemberProjects: [], + organization: desyncOrganization, + queryParams: {statsPeriod: '30d'}, + router: desyncRouter, + shouldEnforceSingleProject: false, + }); + + render(, { + router: desyncRouter, + organization: desyncOrganization, + }); + + // Open menu + await userEvent.click(screen.getByRole('button', {name: '30D', expanded: false})); + + // Desync message is inside the menu + expect(screen.getByText('Filters Updated')).toBeInTheDocument(); + expect( + screen.getByRole('button', {name: 'Restore Previous Values'}) + ).toBeInTheDocument(); + expect(screen.getByRole('button', {name: 'Got It'})).toBeInTheDocument(); + }); +}); diff --git a/static/app/components/codecov/datePicker.tsx b/static/app/components/codecov/datePicker/datePicker.tsx similarity index 85% rename from static/app/components/codecov/datePicker.tsx rename to static/app/components/codecov/datePicker/datePicker.tsx index 226701ec6e76d2..dc6f35fc9a807a 100644 --- a/static/app/components/codecov/datePicker.tsx +++ b/static/app/components/codecov/datePicker/datePicker.tsx @@ -1,14 +1,14 @@ import {useEffect} from 'react'; import {updateDateTime} from 'sentry/actionCreators/pageFilters'; -import type {DateSelectorProps} from 'sentry/components/codecov/dateSelector'; -import {DateSelector} from 'sentry/components/codecov/dateSelector'; +import type {DateSelectorProps} from 'sentry/components/codecov/datePicker/dateSelector'; +import {DateSelector} from 'sentry/components/codecov/datePicker/dateSelector'; import {DesyncedFilterMessage} from 'sentry/components/organizations/pageFilters/desyncedFilter'; import {t} from 'sentry/locale'; import usePageFilters from 'sentry/utils/usePageFilters'; import useRouter from 'sentry/utils/useRouter'; -import {isValidCodecovRelativePeriod} from './utils'; +import {isValidCodecovRelativePeriod} from '../utils'; const CODECOV_DEFAULT_RELATIVE_PERIOD = '24h'; export const CODECOV_DEFAULT_RELATIVE_PERIODS = { @@ -32,7 +32,7 @@ export function DatePicker({ const desynced = desyncedFilters.has('datetime'); const period = selection.datetime?.period; - // Adjusts to valid Codecov relative period + // Adjusts to valid Codecov relative period since Codecov only accepts a subset of dates other components accept, defined in CODECOV_DEFAULT_RELATIVE_PERIODS useEffect(() => { if (!isValidCodecovRelativePeriod(period)) { const newTimePeriod = {period: CODECOV_DEFAULT_RELATIVE_PERIOD}; diff --git a/static/app/components/codecov/datePicker/dateSelector.spec.tsx b/static/app/components/codecov/datePicker/dateSelector.spec.tsx new file mode 100644 index 00000000000000..385de46ec762b9 --- /dev/null +++ b/static/app/components/codecov/datePicker/dateSelector.spec.tsx @@ -0,0 +1,59 @@ +import {ConfigFixture} from 'sentry-fixture/config'; +import {UserFixture} from 'sentry-fixture/user'; + +import {initializeOrg} from 'sentry-test/initializeOrg'; +import {render, screen} from 'sentry-test/reactTestingLibrary'; + +import {DateSelector} from 'sentry/components/codecov/datePicker/dateSelector'; +import ConfigStore from 'sentry/stores/configStore'; + +const {organization, router} = initializeOrg({ + organization: {features: ['global-views', 'open-membership']}, + projects: [ + {id: '1', slug: 'project-1', isMember: true}, + {id: '2', slug: 'project-2', isMember: true}, + {id: '3', slug: 'project-3', isMember: false}, + ], + router: { + location: { + pathname: '/organizations/org-slug/issues/', + query: {}, + }, + params: {}, + }, +}); + +describe('DateSelector', function () { + const onChange = jest.fn(); + + function getComponent(props = {}) { + return ; + } + + function renderComponent(props = {}) { + return render(getComponent(props), {router}); + } + + beforeEach(function () { + ConfigStore.loadInitialData( + ConfigFixture({ + user: UserFixture({ + options: {...UserFixture().options, timezone: 'America/New_York'}, + }), + }) + ); + onChange.mockReset(); + }); + + it('renders when given relative period', async function () { + renderComponent({relative: '7d'}); + expect(await screen.findByRole('button', {name: '7D'})).toBeInTheDocument(); + }); + + it('renders when given an invalid relative period', async function () { + render(, {router, organization}); + expect( + await screen.findByRole('button', {name: 'Invalid Period'}) + ).toBeInTheDocument(); + }); +}); diff --git a/static/app/components/codecov/dateSelector.tsx b/static/app/components/codecov/datePicker/dateSelector.tsx similarity index 100% rename from static/app/components/codecov/dateSelector.tsx rename to static/app/components/codecov/datePicker/dateSelector.tsx diff --git a/static/app/components/codecov/utils.spec.tsx b/static/app/components/codecov/utils.spec.tsx new file mode 100644 index 00000000000000..187470d6f4bf1e --- /dev/null +++ b/static/app/components/codecov/utils.spec.tsx @@ -0,0 +1,16 @@ +import {isValidCodecovRelativePeriod} from 'sentry/components/codecov/utils'; + +describe('isValidCodecovRelativePeriod', function () { + it('returns false for null relative periods', function () { + const period = null; + expect(isValidCodecovRelativePeriod(period)).toBe(false); + }); + it('returns false for periods not belonging to the Codecov default relative periods', function () { + const period = '123d'; + expect(isValidCodecovRelativePeriod(period)).toBe(false); + }); + it('returns true for a valid relative period', function () { + const period = '7d'; + expect(isValidCodecovRelativePeriod(period)).toBe(true); + }); +}); diff --git a/static/app/components/codecov/utils.tsx b/static/app/components/codecov/utils.tsx index f6d28e18531fe2..9c82455c27c47d 100644 --- a/static/app/components/codecov/utils.tsx +++ b/static/app/components/codecov/utils.tsx @@ -1,4 +1,6 @@ -import {CODECOV_DEFAULT_RELATIVE_PERIODS} from './datePicker'; +import {CODECOV_DEFAULT_RELATIVE_PERIODS} from './datePicker/datePicker'; + +// Date Picker Utils Start /** * Determines if a period is valid for a Codecov DatePicker component. A period is invalid if @@ -15,3 +17,5 @@ export function isValidCodecovRelativePeriod(period: string | null): boolean { return true; } + +// Date Picker Utils End diff --git a/static/app/views/codecov/tests/tests.tsx b/static/app/views/codecov/tests/tests.tsx index 6d6e2e317a2d65..e104058370f27c 100644 --- a/static/app/views/codecov/tests/tests.tsx +++ b/static/app/views/codecov/tests/tests.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; -import {DatePicker} from 'sentry/components/codecov/datePicker'; +import {DatePicker} from 'sentry/components/codecov/datePicker/datePicker'; import PageFilterBar from 'sentry/components/organizations/pageFilterBar'; import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container'; import {space} from 'sentry/styles/space'; From 006c03a185422675863f31d70bf78b299c79acc8 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 10 Apr 2025 14:12:50 -0700 Subject: [PATCH 2/3] Adjust tests --- .../codecov/datePicker/datePicker.spec.tsx | 8 ++------ .../codecov/datePicker/dateSelector.spec.tsx | 12 +++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/static/app/components/codecov/datePicker/datePicker.spec.tsx b/static/app/components/codecov/datePicker/datePicker.spec.tsx index ae6b7cd624ef21..c032d38ce5b6b0 100644 --- a/static/app/components/codecov/datePicker/datePicker.spec.tsx +++ b/static/app/components/codecov/datePicker/datePicker.spec.tsx @@ -38,12 +38,11 @@ describe('DatePicker', function () { }); it('can change period', async function () { - render(, {router, organization}); + render(, {router}); await userEvent.click(screen.getByRole('button', {name: '7D', expanded: false})); await userEvent.click(screen.getByRole('option', {name: 'Last 30 days'})); - // Confirm selection changed visible text and query params expect( screen.getByRole('button', {name: '30D', expanded: false}) ).toBeInTheDocument(); @@ -84,7 +83,7 @@ describe('DatePicker', function () { new Set(['datetime']) ); - render(, {router, organization}); + render(, {router}); // Confirm selection changed to default Codecov period const button = await screen.findByRole('button', {name: '24H', expanded: false}); @@ -136,10 +135,7 @@ describe('DatePicker', function () { organization: desyncOrganization, }); - // Open menu await userEvent.click(screen.getByRole('button', {name: '30D', expanded: false})); - - // Desync message is inside the menu expect(screen.getByText('Filters Updated')).toBeInTheDocument(); expect( screen.getByRole('button', {name: 'Restore Previous Values'}) diff --git a/static/app/components/codecov/datePicker/dateSelector.spec.tsx b/static/app/components/codecov/datePicker/dateSelector.spec.tsx index 385de46ec762b9..38b76a5ddf68d6 100644 --- a/static/app/components/codecov/datePicker/dateSelector.spec.tsx +++ b/static/app/components/codecov/datePicker/dateSelector.spec.tsx @@ -7,16 +7,10 @@ import {render, screen} from 'sentry-test/reactTestingLibrary'; import {DateSelector} from 'sentry/components/codecov/datePicker/dateSelector'; import ConfigStore from 'sentry/stores/configStore'; -const {organization, router} = initializeOrg({ - organization: {features: ['global-views', 'open-membership']}, - projects: [ - {id: '1', slug: 'project-1', isMember: true}, - {id: '2', slug: 'project-2', isMember: true}, - {id: '3', slug: 'project-3', isMember: false}, - ], +const {router} = initializeOrg({ router: { location: { - pathname: '/organizations/org-slug/issues/', + pathname: '/codecov/tests/', query: {}, }, params: {}, @@ -51,7 +45,7 @@ describe('DateSelector', function () { }); it('renders when given an invalid relative period', async function () { - render(, {router, organization}); + render(, {router}); expect( await screen.findByRole('button', {name: 'Invalid Period'}) ).toBeInTheDocument(); From 00503b824e6fabb800db05e688d72c156c28d5a9 Mon Sep 17 00:00:00 2001 From: Adrian Date: Thu, 10 Apr 2025 17:35:36 -0700 Subject: [PATCH 3/3] Adjust dateSelector test --- .../codecov/datePicker/dateSelector.spec.tsx | 40 +------------------ 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/static/app/components/codecov/datePicker/dateSelector.spec.tsx b/static/app/components/codecov/datePicker/dateSelector.spec.tsx index 38b76a5ddf68d6..f2a0be2fa9b87a 100644 --- a/static/app/components/codecov/datePicker/dateSelector.spec.tsx +++ b/static/app/components/codecov/datePicker/dateSelector.spec.tsx @@ -1,51 +1,15 @@ -import {ConfigFixture} from 'sentry-fixture/config'; -import {UserFixture} from 'sentry-fixture/user'; - -import {initializeOrg} from 'sentry-test/initializeOrg'; import {render, screen} from 'sentry-test/reactTestingLibrary'; import {DateSelector} from 'sentry/components/codecov/datePicker/dateSelector'; -import ConfigStore from 'sentry/stores/configStore'; - -const {router} = initializeOrg({ - router: { - location: { - pathname: '/codecov/tests/', - query: {}, - }, - params: {}, - }, -}); describe('DateSelector', function () { - const onChange = jest.fn(); - - function getComponent(props = {}) { - return ; - } - - function renderComponent(props = {}) { - return render(getComponent(props), {router}); - } - - beforeEach(function () { - ConfigStore.loadInitialData( - ConfigFixture({ - user: UserFixture({ - options: {...UserFixture().options, timezone: 'America/New_York'}, - }), - }) - ); - onChange.mockReset(); - }); - it('renders when given relative period', async function () { - renderComponent({relative: '7d'}); + render(); expect(await screen.findByRole('button', {name: '7D'})).toBeInTheDocument(); }); it('renders when given an invalid relative period', async function () { - render(, {router}); + render(); expect( await screen.findByRole('button', {name: 'Invalid Period'}) ).toBeInTheDocument();