Skip to content

Commit

Permalink
Add dark mode
Browse files Browse the repository at this point in the history
As a beer league hockey player,
Given it's light outside,
When I open the Wolfpack app,
Then I see a light-themed app,
So that it's easier on my eyes.

As a beer league hockey player,
Given it's dark outside,
When I open the Wolfpack app,
Then I see a dark-themed app,
So that it's easier on my eyes.

https://www.pivotaltracker.com/story/show/186950237

Some other notes:

iOS uses [PlatformColor](https://reactnative.dev/docs/platformcolor)'s,
while other platforms use regular [react native color keywords](https://reactnative.dev/docs/colors#color-keywords).

Because of this, in order to test all the logic for a single color
scheme (we use light), we needed to test the behavior on another
platform (Android and web).

The default platform that we'd been testing was iOS - only iOS.

We now test on iOS and Android.

We would have liked to have tested all of iOS, Android, and web, since
we deploy all three, but we had [trouble getting MSW to work in the web
environment](mswjs/msw#1786).

We tried [the official solution in the docs](https://mswjs.io/docs/migrations/1.x-to-2.x#cannot-find-module-mswnode-jsdom)
and several solutions mentioned in the issue page, linked above. None of
those solutions worked well for us.

In lieu of testing the web env, we're going to try to only do iOS
specific things, and then any other logic that's used should be kept to
work for both Android and the web environment, so that all the web
environment features are still under test in the android test suite. The
solution isn't ideal, but as long as we stick with this approach (and
it'd be easy to forget or not do things this way, which is why it isn't
ideal) all the specs should cover all scenarios in every environment.

It's also worth noting that we decided not to test other color schemes
(we only test light by default) because testing the dark color scheme
seems like it won't add any value, unless we do [snapshot
tests](https://jestjs.io/docs/snapshot-testing), which don't align
well with the spirit of TDD (you write them after the fact) and would
promote difficulting in refactoring (changing the <View> structure of
a page (similar to changing the <div> structure of a webpage)) would
cause a test to fail - which isn't what we're looking for in tests. We
like to know that our feature specs still work after a change is made
- which is to say we like to have confidence that our refactors don't
change behavior, not that they don't change exactly _how_ features are
implemented.
  • Loading branch information
pachun committed Feb 22, 2024
1 parent 151e5ee commit 7dafd1a
Show file tree
Hide file tree
Showing 16 changed files with 352 additions and 166 deletions.
2 changes: 1 addition & 1 deletion app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
Expand Down
34 changes: 20 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,33 +50,39 @@
"typescript": "^5.1.3"
},
"jest": {
"preset": "jest-expo",
"projects": [
{
"preset": "jest-expo/ios",
"setupFilesAfterEnv": [
"./spec/specHelpers/jest/setupFilesAfterEnv/useReactNativeSpecificJestMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/jestCustomMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/mockExpoLibraries.ts"
]
},
{
"preset": "jest-expo/android",
"setupFilesAfterEnv": [
"./spec/specHelpers/jest/setupFilesAfterEnv/useReactNativeSpecificJestMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/jestCustomMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/mockExpoLibraries.ts"
]
}
],
"transformIgnorePatterns": [
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
],
"setupFilesAfterEnv": [
"./spec/specHelpers/jest/setupFilesAfterEnv/useReactNativeSpecificJestMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/jestCustomMatchers.ts",
"./spec/specHelpers/jest/setupFilesAfterEnv/mockExpoLibraries.ts"
],
"verbose": true,
"passWithNoTests": true,
"coverageProvider": "v8",
"collectCoverageFrom": [
"./src/**",
"!src/Config.ts"
],
"collectCoverageFrom": ["./src/**", "!src/Config.ts", "!src/types/**"],
"coverageThreshold": {
"global": {
"lines": 100,
"functions": 100,
"branches": 100,
"statements": 100
}
},
"coveragePathIgnorePatterns": [
"./src/types"
]
}
},
"private": true
}
83 changes: 51 additions & 32 deletions spec/app/games.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ERTL from "expo-router/testing-library"
import * as ReactNative from "react-native"
import * as DateFNS from "date-fns"
import type { Game } from "types/Game"
import gameFactory from "../specHelpers/factories/game"
Expand Down Expand Up @@ -28,7 +29,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).toShowTestID("Loading Spinner")
expect(ERTL.screen).toShowTestId("Loading Spinner")
})
},
})
Expand Down Expand Up @@ -69,7 +70,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

const gameListItems = ERTL.screen.getAllByTestId("Game List Item")
Expand All @@ -94,7 +95,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

const gameListItems = ERTL.screen.getAllByTestId("Game List Item")
Expand Down Expand Up @@ -126,7 +127,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

const gameListItems = ERTL.screen.getAllByTestId("Game List Item")
Expand Down Expand Up @@ -168,7 +169,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

const gameListItems = ERTL.screen.getAllByTestId("Game List Item")
Expand All @@ -177,18 +178,26 @@ describe("viewing the games tab", () => {
expect(ERTL.within(gameListItems[0])).toShowText("2 - 1 W")
expect(ERTL.within(gameListItems[1])).toShowText("0 - 3 L")
expect(ERTL.within(gameListItems[2])).toShowText("0 - 0 T")
expect(
ERTL.within(gameListItems[0]).getByText("2 - 1 W").props.style
.color,
).toEqual("green")
expect(
ERTL.within(gameListItems[1]).getByText("0 - 3 L").props.style
.color,
).toEqual("red")
expect(
ERTL.within(gameListItems[2]).getByText("0 - 0 T").props.style
.color,
).toEqual("gray")

const winLabelColor = ERTL.within(gameListItems[0]).getByText(
"2 - 1 W",
).props.style.color
const lossLabelColor = ERTL.within(gameListItems[1]).getByText(
"0 - 3 L",
).props.style.color
const tieLabelColor = ERTL.within(gameListItems[2]).getByText(
"0 - 0 T",
).props.style.color

if (ReactNative.Platform.OS === "ios") {
expect(winLabelColor).toEqual({ semantic: ["systemGreen"] })
expect(lossLabelColor).toEqual({ semantic: ["systemRed"] })
expect(tieLabelColor).toEqual({ semantic: ["systemGray"] })
} else {
expect(winLabelColor).toEqual("green")
expect(lossLabelColor).toEqual("red")
expect(tieLabelColor).toEqual("gray")
}
})
},
})
Expand All @@ -209,7 +218,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

await ERTL.waitFor(() => {
Expand All @@ -231,7 +240,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})

const gameListItems = ERTL.screen.getAllByTestId("Game List Item")
Expand All @@ -255,7 +264,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})
},
})
Expand All @@ -270,7 +279,7 @@ describe("viewing the games tab", () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => {
expect(ERTL.screen).not.toShowTestID("Loading Spinner")
expect(ERTL.screen).not.toShowTestId("Loading Spinner")
})
},
})
Expand Down Expand Up @@ -324,7 +333,9 @@ describe("viewing the games tab", () => {
test: async () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => expect(ERTL.screen).toShowText("Reload"))
await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestId("Reload Button"),
)
},
})
})
Expand All @@ -336,14 +347,16 @@ describe("viewing the games tab", () => {
test: async () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => expect(ERTL.screen).toShowText("Reload"))
await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestId("Reload Button"),
)
},
})

await mockGamesFromApi({
response: "Network Error",
test: async () => {
ERTL.fireEvent.press(ERTL.screen.getByText("Reload"))
ERTL.fireEvent.press(ERTL.screen.getByTestId("Reload Button"))

await ERTL.waitFor(() =>
expect(ERTL.screen).not.toShowText(
Expand All @@ -360,17 +373,19 @@ describe("viewing the games tab", () => {
test: async () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => expect(ERTL.screen).toShowText("Reload"))
await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestId("Reload Button"),
)
},
})

await mockGamesFromApi({
response: "Network Error",
test: async () => {
ERTL.fireEvent.press(ERTL.screen.getByText("Reload"))
ERTL.fireEvent.press(ERTL.screen.getByTestId("Reload Button"))

await ERTL.waitFor(() =>
expect(ERTL.screen).not.toShowText("Reload"),
expect(ERTL.screen).not.toShowTestId("Reload Button"),
)
},
})
Expand All @@ -382,17 +397,19 @@ describe("viewing the games tab", () => {
test: async () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => expect(ERTL.screen).toShowText("Reload"))
await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestId("Reload Button"),
)
},
})

await mockGamesFromApi({
response: "Network Error",
test: async () => {
ERTL.fireEvent.press(ERTL.screen.getByText("Reload"))
ERTL.fireEvent.press(ERTL.screen.getByTestId("Reload Button"))

await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestID("Loading Spinner"),
expect(ERTL.screen).toShowTestId("Loading Spinner"),
)
},
})
Expand All @@ -405,7 +422,9 @@ describe("viewing the games tab", () => {
test: async () => {
ERTL.renderRouter("src/app", { initialUrl: "/games" })

await ERTL.waitFor(() => expect(ERTL.screen).toShowText("Reload"))
await ERTL.waitFor(() =>
expect(ERTL.screen).toShowTestId("Reload Button"),
)
},
})

Expand All @@ -414,7 +433,7 @@ describe("viewing the games tab", () => {
await mockGamesFromApi({
response: [game],
test: async () => {
ERTL.fireEvent.press(ERTL.screen.getByText("Reload"))
ERTL.fireEvent.press(ERTL.screen.getByTestId("Reload Button"))

await ERTL.waitFor(() => {
expect(ERTL.screen).toShowText(gameDate(game))
Expand Down
Loading

0 comments on commit 7dafd1a

Please sign in to comment.