Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds synonyms moderation front end #2538

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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
Courey marked this conversation as resolved.
Show resolved Hide resolved
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 @@
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 @@
putVersion,
search,
} from '../circulars/circulars.server'
import CircularPagination from './CircularPagination'
lpsinger marked this conversation as resolved.
Show resolved Hide resolved
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 @@
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
Loading