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

test setup #74 #75

Closed
2 changes: 1 addition & 1 deletion lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
'*.+(js|jsx|ts|tsx)': ['eslint --fix'],
'*.+(js|jsx|ts|tsx)': ['eslint --fix', 'yarn test:ci'],
'*.+(json|yml|yaml|css)': ['prettier --write'],
}
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"scripts": {
"start": "yarn start:firestore & yarn start:dev",
"start": "run-p start:firestore start:dev",
"start:dev": "snowpack dev",
"start:firestore": "firebase emulators:start --import=./devData --only firestore",
"build": "snowpack build",
"test": "jest --watch",
"test": "jest --watch --FIRESTORE_EMULATOR_HOST=8080",
AhmedEldessouki marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when i started testing my app (which is also) using firebase. I used to get my API like this [...API_KEY]. so how did dotenv.config(); fix it? ... i used to think that Kent did something so people like me wouldn't do the mistake of testing using the same backend the app uses.

Dotenv just imported whatever I had in my .env file, as without those, it would fall over. This also meant that I don't have to worry about var being added or removed. It becomes extremely(!!!!) important to make sure you aren't "leaking" anything out to the real world, however. In Scrivener, I am managing this by using MSW with a catchall:

  rest.get('*', (req, res, ctx) => {
    const warning = `${req.url} has not been mocked yet... Maybe now is a good time to do that`;
    console.warn(warning);
    return res(ctx.json(warning));
  }),

And then firestore has this really nice feature of the FIRESTORE_EMULATOR_HOST env var, which will make it point to the emulator instead of real firebase.

Copy link
Contributor Author

@AhmedEldessouki AhmedEldessouki Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this would NOT work. run-s runs things sequentially, and the firestore emulator just runs until stopped - so it would never run the test script.

I thought it will run firestore emulator then jest. but even run-p didn't work even thu it worked with start command.

components/loadingScreen/loadingScreen.test.ts <-- this will place it right next to the file it is testing.

this will surely be a lot cleaner. I will do the changes.
@JacobMGEvans is it okay to do so?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see now. that cleared alot of things for me. 🙏🏼 thanks 🙂
using the emulator is way better than using MSW to fake the requests. because firebase url are pretty damn big.

Suggested change
"test": "jest --watch --FIRESTORE_EMULATOR_HOST=8080",
"test": "jest --watch",

I dont think that the FIRESTORE_EMULATOR_HOST var works here in this line. when i added it, it was for the test env to pick up on it but the way you wrote the script and configured the env var bypassed its use case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AhmedEldessouki the way you set it here doesn't set an env var. You will have to use something like cross-env and set it first - see Guild Scrivener for how I setup a generic "execute firestore" command that everything else hooks off of for a solid example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for pointing that out 🙂 I will look for it 😁😁

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant work around this. int the Guild Scrivener you are using Node env. do you think it's possible for @firebase/testing to work in Jsdom env?
Error: FIRESTORE (8.2.4) INTERNAL ASSERTION FAILED: Unexpected state

I ran into this problem before when i was testing firestore-rules (Node env). after i worked around it, jsdom tests was being unexpected and then i removed firestore-rules test

"test:ci": "jest",
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
"lint": "eslint --fix --ext js,jsx,ts,tsx --ignore-path .gitignore .",
Expand All @@ -31,7 +31,10 @@
"@storybook/addon-links": "^6.1.15",
"@storybook/react": "^6.1.15",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.0.0",
"@testing-library/react": "^11.2.3",
"@testing-library/react-hooks": "^5.0.3",
"@testing-library/user-event": "^12.6.2",
"@types/faker": "^5.1.6",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.7",
Expand All @@ -46,10 +49,12 @@
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"faker": "^5.2.0",
"firebase-tools": "^9.2.2",
"husky": "^4.3.8",
"jest": "^26.6.3",
"lint-staged": "^10.5.3",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5",
"snowpack": "^3.0.1",
"typescript": "^4.0.0"
Expand Down
30 changes: 30 additions & 0 deletions src/__tests__/AllRaids.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react'
import { loadingScreen, render, screen, userEvent } from '../tests/testUtils'
import App from '../App'
import { fetchedCollectionData } from '../tests/data/raidsData'

beforeAll(() => {
jest.doMock('../utils/useCollection', () =>
jest.fn().mockReturnValue(fetchedCollectionData),
)
})
afterAll(() => {
jest.resetAllMocks()
})

it('should AllRaids links', async () => {
AhmedEldessouki marked this conversation as resolved.
Show resolved Hide resolved
render(<App />)

userEvent.click(screen.getByText('Raids'))

await loadingScreen()

expect(screen.getByText('Raids')).toBeInTheDocument()
expect(screen.getByText('Active')).toBeInTheDocument()
expect(screen.getByText('Completed')).toBeInTheDocument()

const data = fetchedCollectionData.data
data.forEach((s) =>
expect(screen.getByText(`${s.title} | ${s.dungeon}`)).toBeInTheDocument(),
)
})
40 changes: 38 additions & 2 deletions src/__tests__/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
import * as React from 'react'
import { render } from '@testing-library/react'
import { loadingScreen, render, screen, userEvent } from '../tests/testUtils'
import App from '../App'
import {
fetchedCollectionData,
fetchedDocumentData,
} from '../tests/data/raidsData'

it('renders without crashing', () => {
beforeAll(() => {
jest.doMock('../utils/useDocument', () =>
jest.fn().mockReturnValue(fetchedDocumentData),
)
jest.doMock('../utils/useCollection', () =>
jest.fn().mockReturnValue(fetchedCollectionData),
)
})
afterAll(() => {
jest.resetAllMocks()
})

it('can view all raids and individual raids', async () => {
render(<App />)

userEvent.click(screen.getByText('Raids'))

await loadingScreen()

const data = fetchedCollectionData.data

expect(screen.getByText('Raids')).toBeInTheDocument()

data.forEach((s) =>
expect(screen.getByText(`${s.title} | ${s.dungeon}`)).toBeInTheDocument(),
)

userEvent.click(screen.getByText(`${data[0].title} | ${data[0].dungeon}`))

await loadingScreen()

expect(
screen.getByText(`${fetchedDocumentData.data.title}`),
).toBeInTheDocument()
})
69 changes: 69 additions & 0 deletions src/__tests__/Feedback.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react'
import { render, screen, userEvent } from '../tests/testUtils'
import faker from 'faker'
import App from '../App'
import {
fetchedCollectionData,
fetchedDocumentData,
} from '../tests/data/raidsData'

const buildFeedback = {
name: faker.name.firstName(),
repoName: faker.commerce.productName(),
description: faker.lorem.paragraph(10),
rating: Math.round(faker.random.number({ min: 1, max: 5 })),
}

beforeAll(() => {
jest.doMock('../utils/useDocument', () =>
jest.fn().mockReturnValue(fetchedDocumentData),
)
jest.doMock('../utils/useCollection', () =>
jest.fn().mockReturnValue(fetchedCollectionData),
)
jest.spyOn(console, 'log')
Comment on lines +18 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we use the emulators, we don't have to mock anything because we are just interfacing with the emulator instead of the real thing - yes you still have to "seed" the data.

This can be done by just importing from the devData dir for now, as the website is really read-only.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we use the emulators, we don't have to mock anything

totally agree. i tried it before mocking but when it failed i when with mocking. i will do remove the mocks.

})
afterAll(() => {
jest.resetAllMocks()
})

it('should renders and visits Raids then the first Link', async () => {
render(<App />)
userEvent.click(screen.getByText('Feedback'))

expect(screen.getByLabelText(`3`)).toBeChecked()
userEvent.type(screen.getByPlaceholderText('Your name'), buildFeedback.name)
userEvent.type(
screen.getByPlaceholderText('Repo name'),
buildFeedback.repoName,
)
// Both didn't work
// fireEvent.change(
// screen.getByLabelText(`${buildFeedback.rating}`),
// `${buildFeedback.rating}`,
// )

// userEvent.selectOptions(
// screen.getByRole('radiogroup'),
// `${buildFeedback.rating}`,
// )

userEvent.click(screen.getByText(`${buildFeedback.rating}`))

userEvent.type(
screen.getByLabelText('Description'),
buildFeedback.description,
)
expect(screen.getByPlaceholderText('Your name')).toHaveDisplayValue(
buildFeedback.name,
)
expect(screen.getByPlaceholderText('Repo name')).toHaveDisplayValue(
buildFeedback.repoName,
)
expect(screen.getByLabelText('Description')).toHaveDisplayValue(
buildFeedback.description,
)
expect(screen.getByLabelText(`${buildFeedback.rating}`)).toBeChecked()

userEvent.click(screen.getByText('Submit'))
})
116 changes: 116 additions & 0 deletions src/__tests__/Raids.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*eslint no-irregular-whitespace: ["error", { "skipTemplates": true }]*/
import * as React from 'react'
import { loadingScreen, render, screen, userEvent } from '../tests/testUtils'
import App from '../App'
import {
fetchedCollectionData,
fetchedDocumentData,
} from '../tests/data/raidsData'

// Note: After each Test Jest doesn't un-mount `The Component` Thats why we don't need to re-visit the page every time

beforeAll(() => {
jest.doMock('../utils/useDocument', () =>
jest.fn().mockReturnValue(fetchedDocumentData),
)
jest.doMock('../utils/useCollection', () =>
jest.fn().mockReturnValue(fetchedCollectionData),
)
})
afterAll(() => {
jest.resetAllMocks()
})
const data = fetchedDocumentData.data

it('should test the ViewRaid $TotalStats', async () => {
AhmedEldessouki marked this conversation as resolved.
Show resolved Hide resolved
render(<App />)

userEvent.click(screen.getByText('Raids'))

await loadingScreen()

userEvent.click(
screen.getByText(
`${fetchedCollectionData.data[0].title} | ${fetchedCollectionData.data[0].dungeon}`,
),
)
await loadingScreen()

expect(
screen.getByText(`${Object.keys(data.contributors).length} Contributors`),
).toBeInTheDocument()
expect(screen.getByText(`${data.commits} Commits`)).toBeInTheDocument()
expect(
screen.getByText(`${data.changedFiles} Changed Files`),
).toBeInTheDocument()
expect(
screen.getByText(`+${data.additions} -${data.deletions}`),
).toBeInTheDocument()
})
it('should Sort ViewRaid By Addition', async () => {
render(<App />)

const contributorsList = screen.getAllByRole('listitem')

userEvent.selectOptions(screen.getByRole('combobox'), 'additions')

Object.values(data.contributors)
.sort((a, b) => b.additions - a.additions)
.forEach((contributor, index) => {
// 4 is for the 4 list items of $TotalStats
const commitAmount = contributor.commits > 1 ? 'Commits' : 'Commit'
expect(screen.getByAltText(`${contributor.user}'s avatar`)).toBeTruthy()
expect(contributorsList[index + 4]).toContainHTML(`#${index + 1}`)
expect(screen.getByText(contributor.user)).toBeInTheDocument()
expect(contributorsList[index + 4]).toContainHTML(
`+${contributor.additions} -${contributor.deletions}`,
)
expect(
screen.getByText(`${contributor.commits} ${commitAmount}`),
).toBeInTheDocument()
})
})
it('should Sort ViewRaid By Commits', async () => {
render(<App />)

const contributorsList = screen.getAllByRole('listitem')

userEvent.selectOptions(screen.getByRole('combobox'), 'commits')

Object.values(data.contributors)
.sort((a, b) => b.commits - a.commits)
.forEach((contributor, index) => {
// 4 is for the 4 list items of $TotalStats
const commitAmount = contributor.commits > 1 ? 'Commits' : 'Commit'
expect(contributorsList[index + 4]).toContainHTML(`#${index + 1}`)
expect(screen.getByText(contributor.user)).toBeInTheDocument()
expect(contributorsList[index + 4]).toContainHTML(
`+${contributor.additions} -${contributor.deletions}`,
)
expect(
screen.getByText(`${contributor.commits} ${commitAmount}`),
).toBeInTheDocument()
})
})
it('should Sort ViewRaid By Deletions', async () => {
render(<App />)

const contributorsList = screen.getAllByRole('listitem')

userEvent.selectOptions(screen.getByRole('combobox'), 'deletions')

Object.values(data.contributors)
.sort((a, b) => b.deletions - a.deletions)
.forEach((contributor, index) => {
// 4 is for the 4 list items of $TotalStats
const commitAmount = contributor.commits > 1 ? 'Commits' : 'Commit'
expect(contributorsList[index + 4]).toContainHTML(`#${index + 1}`)
expect(screen.getByText(contributor.user)).toBeInTheDocument()
expect(contributorsList[index + 4]).toContainHTML(
`+${contributor.additions} -${contributor.deletions}`,
)
expect(
screen.getByText(`${contributor.commits} ${commitAmount}`),
).toBeInTheDocument()
})
})
22 changes: 14 additions & 8 deletions src/components/feedback/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ function Feedback() {
raidName,
rate,
description,
} = e.target as typeof e.target & {
} = e.currentTarget as typeof e.currentTarget & {
name: { value: string }
raidName: { value: string }
rate: { value: string }
description: { value: string }
}
console.log({
name: name.value,
raidName: raidName.value,
rate: rate.value,
description: description.value,
})
// console.log({
// name: name.value,
// raidName: raidName.value,
// rate: rate.value,
// description: description.value,
// })
Comment on lines +21 to +26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean up later

}

return (
Expand Down Expand Up @@ -59,7 +59,13 @@ function Feedback() {
<label htmlFor="rate1">1</label>
<input type="radio" value="fine" name="rate" id="rate2" />
<label htmlFor="rate2">2</label>
<input type="radio" value="superior" name="rate" id="rate3" />
<input
type="radio"
value="superior"
name="rate"
id="rate3"
defaultChecked
/>
<label htmlFor="rate3">3</label>
<input type="radio" value="epic" name="rate" id="rate4" />
<label htmlFor="rate4">4</label>
Expand Down
2 changes: 1 addition & 1 deletion src/components/loadingSpinner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import styled from '@emotion/styled'
export default function LoadingSpinner() {
return (
<Background>
<Ellipsis>
<Ellipsis aria-label="loadingScreen">
<div></div>
<div></div>
<div></div>
Expand Down
2 changes: 1 addition & 1 deletion src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
import '@testing-library/jest-dom'
import '@testing-library/jest-dom/extend-expect'
Loading