Skip to content

Commit

Permalink
adds synonyms moderation front end
Browse files Browse the repository at this point in the history
adds validation and error handling

fixes tests

adds modal warning prior to delete

changes removal button to words instead of only icons

code review change requests

removing unused className

formatting

autofill moderator synonym eventId selector

adding create sad path test

removing feature flag check

adding a 3 second debounce
  • Loading branch information
Courey committed Oct 9, 2024
1 parent 0fa60e5 commit 57134ec
Show file tree
Hide file tree
Showing 8 changed files with 772 additions and 157 deletions.
162 changes: 150 additions & 12 deletions __tests__/synonyms.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,65 @@ import type { AWSError, DynamoDB } from 'aws-sdk'
import * as awsSDKMock from 'aws-sdk-mock'
import crypto from 'crypto'

import type { Circular } from '~/routes/circulars/circulars.lib'
import { createSynonyms, putSynonyms } from '~/routes/synonyms/synonyms.server'

jest.mock('@architect/functions')
const synonymId = 'abcde-abcde-abcde-abcde-abcde'
const exampleCirculars = [
{
Items: [
{
circularId: 1234556,
subject: 'subject 1',
body: 'very intelligent things',
eventId: 'eventId1',
createdOn: 12345567,
submitter: 'steve',
} as Circular,
],
},
{
Items: [
{
circularId: 1230000,
subject: 'subject 2',
body: 'more intelligent things',
eventId: 'eventId2',
createdOn: 12345560,
submitter: 'steve',
} as Circular,
],
},
{ Items: [] },
]

describe('createSynonyms', () => {
beforeAll(() => {
beforeEach(() => {
const mockBatchWrite = jest.fn()
const mockQuery = jest.fn()

const mockClient = {
batchWrite: mockBatchWrite,
query: mockQuery,
}
;(tables as unknown as jest.Mock).mockResolvedValue({

;(tables as unknown as jest.Mock).mockReturnValue({
_doc: mockClient,
name: () => {
return 'synonyms'
},
circulars: {
query: mockQuery
.mockReturnValueOnce(exampleCirculars[0])
.mockReturnValueOnce(exampleCirculars[1]),
},
})

jest.spyOn(crypto, 'randomUUID').mockReturnValue(synonymId)
})

afterAll(() => {
afterEach(() => {
jest.restoreAllMocks()
})

Expand All @@ -48,12 +85,51 @@ describe('createSynonyms', () => {

expect(result).toBe(synonymId)
})

test('createSynonyms with nonexistent eventId throws Response 400', async () => {
const mockBatchWriteItem = jest.fn(
(
params: DynamoDB.DocumentClient.BatchWriteItemInput,
callback: (
err: AWSError | null,
data?: DynamoDB.DocumentClient.BatchWriteItemOutput
) => void
) => {
expect(params.RequestItems.synonyms).toBeDefined()
callback(null, {})
}
)
awsSDKMock.mock('DynamoDB', 'batchWriteItem', mockBatchWriteItem)

const synonymousEventIds = ['eventId1', 'nope']
try {
await createSynonyms(synonymousEventIds)
} catch (error) {
// eslint-disable-next-line jest/no-conditional-expect
expect(error).toBeInstanceOf(Response)
const convertedError = error as Response
// eslint-disable-next-line jest/no-conditional-expect
expect(convertedError.status).toBe(400)
const errorMessage = await convertedError.text()
// eslint-disable-next-line jest/no-conditional-expect
expect(errorMessage).toBe('eventId does not exist')
}
})
})

describe('putSynonyms', () => {
const mockBatchWrite = jest.fn()
const mockQuery = jest.fn()

beforeAll(() => {
jest.spyOn(crypto, 'randomUUID').mockReturnValue(synonymId)
})

afterAll(() => {
jest.restoreAllMocks()
awsSDKMock.restore('DynamoDB')
})
test('putSynonyms should not write to DynamoDB if no additions or subtractions', async () => {
const mockClient = {
batchWrite: mockBatchWrite,
}
Expand All @@ -63,23 +139,60 @@ describe('putSynonyms', () => {
return 'synonyms'
},
})

jest.spyOn(crypto, 'randomUUID').mockReturnValue(synonymId)
})

afterAll(() => {
jest.restoreAllMocks()
awsSDKMock.restore('DynamoDB')
})
test('putSynonyms should not write to DynamoDB if no additions or subtractions', async () => {
awsSDKMock.mock('DynamoDB.DocumentClient', 'batchWrite', mockBatchWrite)

await putSynonyms({ synonymId })

expect(mockBatchWrite).not.toHaveBeenCalled()
})

test('putSynonyms should throw 400 response if there are invalid additions', async () => {
const mockClient = {
batchWrite: mockBatchWrite,
query: mockQuery,
}

;(tables as unknown as jest.Mock).mockReturnValue({
_doc: mockClient,
name: () => {
return 'synonyms'
},
circulars: {
query: mockQuery.mockReturnValueOnce(exampleCirculars[2]),
},
})
awsSDKMock.mock('DynamoDB.DocumentClient', 'batchWrite', mockBatchWrite)
try {
await putSynonyms({ synonymId, additions: ["doesn't exist"] })
} catch (error) {
// eslint-disable-next-line jest/no-conditional-expect
expect(error).toBeInstanceOf(Response)
const convertedError = error as Response
// eslint-disable-next-line jest/no-conditional-expect
expect(convertedError.status).toBe(400)
const errorMessage = await convertedError.text()
// eslint-disable-next-line jest/no-conditional-expect
expect(errorMessage).toBe('eventId does not exist')
}
})

test('putSynonyms should write to DynamoDB if there are additions', async () => {
const mockClient = {
batchWrite: mockBatchWrite,
query: mockQuery,
}

;(tables as unknown as jest.Mock).mockReturnValue({
_doc: mockClient,
name: () => {
return 'synonyms'
},
circulars: {
query: mockQuery
.mockReturnValueOnce(exampleCirculars[0])
.mockReturnValueOnce(exampleCirculars[1]),
},
})
awsSDKMock.mock('DynamoDB.DocumentClient', 'batchWrite', mockBatchWrite)
const additions = ['eventId1', 'eventId2']
await putSynonyms({ synonymId, additions })
Expand Down Expand Up @@ -109,6 +222,15 @@ describe('putSynonyms', () => {
})

test('putSynonyms should write to DynamoDB if there are subtractions', async () => {
const mockClient = {
batchWrite: mockBatchWrite,
}
;(tables as unknown as jest.Mock).mockResolvedValue({
_doc: mockClient,
name: () => {
return 'synonyms'
},
})
awsSDKMock.mock('DynamoDB.DocumentClient', 'batchWrite', mockBatchWrite)

const subtractions = ['eventId3', 'eventId4']
Expand All @@ -126,6 +248,22 @@ describe('putSynonyms', () => {
})

test('putSynonyms should write to DynamoDB if there are additions and subtractions', async () => {
const mockClient = {
batchWrite: mockBatchWrite,
query: mockQuery,
}

;(tables as unknown as jest.Mock).mockReturnValue({
_doc: mockClient,
name: () => {
return 'synonyms'
},
circulars: {
query: mockQuery
.mockReturnValueOnce(exampleCirculars[0])
.mockReturnValueOnce(exampleCirculars[1]),
},
})
awsSDKMock.mock('DynamoDB.DocumentClient', 'batchWrite', mockBatchWrite)

const additions = ['eventId1', 'eventId2']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function getPageLink({
return searchString && `?${searchString}`
}

export default function ({
export default function GCNPagination({

Check warning on line 38 in app/components/pagination/GCNPagination.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/GCNPagination.tsx#L38

Added line #L38 was not covered by tests
page,
totalPages,
...queryStringProps
Expand Down
61 changes: 61 additions & 0 deletions app/components/pagination/PaginationSelectionFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*!
* Copyright © 2023 United States Government as represented by the
* Administrator of the National Aeronautics and Space Administration.
* All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*/
import { useSubmit } from '@remix-run/react'
import { Select } from '@trussworks/react-uswds'

Check warning on line 9 in app/components/pagination/PaginationSelectionFooter.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/PaginationSelectionFooter.tsx#L8-L9

Added lines #L8 - L9 were not covered by tests

import GCNPagination from './GCNPagination'

Check warning on line 11 in app/components/pagination/PaginationSelectionFooter.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/PaginationSelectionFooter.tsx#L11

Added line #L11 was not covered by tests

export default function PaginationSelectionFooter({

Check warning on line 13 in app/components/pagination/PaginationSelectionFooter.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/PaginationSelectionFooter.tsx#L13

Added line #L13 was not covered by tests
page,
totalPages,
limit,
query,
form,
}: {
page: number
totalPages: number
limit?: number
query?: string
form: string
}) {
const submit = useSubmit()
return (

Check warning on line 27 in app/components/pagination/PaginationSelectionFooter.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/PaginationSelectionFooter.tsx#L26-L27

Added lines #L26 - L27 were not covered by tests
<div className="display-flex flex-row flex-wrap">
<div className="display-flex flex-align-self-center margin-right-2 width-auto">
<div>
<Select
id="limit"
title="Number of results per page"
className="width-auto height-5 padding-y-0 margin-y-0"
name="limit"
defaultValue="100"
form={form}
onChange={({ target: { form } }) => {
submit(form)

Check warning on line 39 in app/components/pagination/PaginationSelectionFooter.tsx

View check run for this annotation

Codecov / codecov/patch

app/components/pagination/PaginationSelectionFooter.tsx#L38-L39

Added lines #L38 - L39 were not covered by tests
}}
>
<option value="10">10 / page</option>
<option value="20">20 / page</option>
<option value="50">50 / page</option>
<option value="100">100 / page</option>
</Select>
</div>
</div>
<div className="display-flex flex-fill">
{totalPages > 1 && (
<GCNPagination
query={query}
page={page}
limit={limit}
totalPages={totalPages}
/>
)}
</div>
</div>
)
}
52 changes: 9 additions & 43 deletions app/routes/circulars._archive._index/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@ import {
useSearchParams,
useSubmit,
} from '@remix-run/react'
import {
Alert,
Button,
Icon,
Label,
Select,
TextInput,
} from '@trussworks/react-uswds'
import { Alert, Button, Icon, Label, TextInput } from '@trussworks/react-uswds'

Check warning on line 17 in app/routes/circulars._archive._index/route.tsx

View check run for this annotation

Codecov / codecov/patch

app/routes/circulars._archive._index/route.tsx#L17

Added line #L17 was not covered by tests
import clamp from 'lodash/clamp'
import { useId, useState } from 'react'

Expand All @@ -41,13 +34,13 @@ import {
putVersion,
search,
} from '../circulars/circulars.server'
import CircularPagination from './CircularPagination'
import CircularsHeader from './CircularsHeader'
import CircularsIndex from './CircularsIndex'
import { DateSelector } from './DateSelectorMenu'
import { SortSelector } from './SortSelectorButton'
import Hint from '~/components/Hint'
import { ToolbarButtonGroup } from '~/components/ToolbarButtonGroup'
import PaginationSelectionFooter from '~/components/pagination/PaginationSelectionFooter'

Check warning on line 43 in app/routes/circulars._archive._index/route.tsx

View check run for this annotation

Codecov / codecov/patch

app/routes/circulars._archive._index/route.tsx#L43

Added line #L43 was not covered by tests
import { origin } from '~/lib/env.server'
import { getFormDataString } from '~/lib/utils'
import { postZendeskRequest } from '~/lib/zendesk.server'
Expand Down Expand Up @@ -281,40 +274,13 @@ export default function () {
totalItems={totalItems}
query={query}
/>
<div className="display-flex flex-row flex-wrap">
<div className="display-flex flex-align-self-center margin-right-2 width-auto">
<div>
<Select
id="limit"
title="Number of results per page"
className="width-auto height-5 padding-y-0 margin-y-0"
name="limit"
defaultValue="100"
form={formId}
onChange={({ target: { form } }) => {
submit(form)
}}
>
<option value="10">10 / page</option>
<option value="20">20 / page</option>
<option value="50">50 / page</option>
<option value="100">100 / page</option>
</Select>
</div>
</div>
<div className="display-flex flex-fill">
{totalPages > 1 && (
<CircularPagination
query={query}
page={page}
limit={parseInt(limit)}
totalPages={totalPages}
startDate={startDate}
endDate={endDate}
/>
)}
</div>
</div>
<PaginationSelectionFooter
query={query}
page={page}
limit={parseInt(limit)}
totalPages={totalPages}
form={formId}
/>
</>
)}
</>
Expand Down
Loading

0 comments on commit 57134ec

Please sign in to comment.