Add pretty formatting (#29)

* Test

* Add pretty formatting

* Fix Tests

* Fix Tests

* Fix Tests

* Fix

* Add pretty formatting fix

* Fix

* Test

* Fix tests

* Clean typeckech

* Add prettier check

* Fix api tsconfig

* Fix api tsconfig

* Fix tsconfig

* Fix

* Fix

* Prettier
This commit is contained in:
Martin Braquet
2026-02-20 17:32:27 +01:00
committed by GitHub
parent 1994697fa1
commit ba9b3cfb06
695 changed files with 22382 additions and 23209 deletions

View File

@@ -12,9 +12,9 @@ body:
attributes:
label: Info
description: |
- Browser: [e.g. chrome, safari]
- Device (if mobile): [e.g. iPhone6]
- Build info
- Browser: [e.g. chrome, safari]
- Device (if mobile): [e.g. iPhone6]
- Build info
placeholder: |
Build info from `Settings` -> `About`
validations:

View File

@@ -4,5 +4,5 @@
- [ ] Tests added and passed if fixing a bug or adding a new feature.
### Description
<!-- Describe your changes in detail -->
<!-- Describe your changes in detail -->

View File

@@ -7,17 +7,17 @@ globs:
## Project Structure
- next.js react tailwind frontend `/web`
- broken down into pages, components, hooks, lib
- broken down into pages, components, hooks, lib
- express node api server `/backend/api`
- one off scripts, like migrations `/backend/scripts`
- supabase postgres. schema in `/backend/supabase`
- supabase-generated types in `/backend/supabase/schema.ts`
- supabase-generated types in `/backend/supabase/schema.ts`
- files shared between backend directories `/backend/shared`
- anything in `/backend` can import from `shared`, but not vice versa
- anything in `/backend` can import from `shared`, but not vice versa
- files shared between the frontend and backend in `/common`
- `/common` has lots of type definitions for our data structures, like User. It also contains many useful utility
functions. We try not to add package dependencies to common. `/web` and `/backend` are allowed to import from
`/common`, but not vice versa.
- `/common` has lots of type definitions for our data structures, like User. It also contains many useful utility
functions. We try not to add package dependencies to common. `/web` and `/backend` are allowed to import from
`/common`, but not vice versa.
## Deployment
@@ -52,18 +52,11 @@ export function HeadlineTabs(props: {
notSticky?: boolean
className?: string
}) {
const {headlines, endpoint, currentSlug, hideEmoji, notSticky, className} =
props
const {headlines, endpoint, currentSlug, hideEmoji, notSticky, className} = props
const user = useUser()
return (
<div
className={clsx(
className,
'bg-canvas-50 w-full',
!notSticky && 'sticky top-0 z-50'
)}
>
<div className={clsx(className, 'bg-canvas-50 w-full', !notSticky && 'sticky top-0 z-50')}>
<Carousel labelsParentClassName="gap-px">
{headlines.map(({id, slug, title}) => (
<Tab
@@ -73,9 +66,9 @@ export function HeadlineTabs(props: {
active={slug === currentSlug}
/>
))}
{user && <Tab label="More" href="/dashboard"/>}
{user && <Tab label="More" href="/dashboard" />}
{user && (isAdminId(user.id) || isModId(user.id)) && (
<EditNewsButton endpoint={endpoint} defaultDashboards={headlines}/>
<EditNewsButton endpoint={endpoint} defaultDashboards={headlines} />
)}
</Carousel>
</div>
@@ -146,9 +139,7 @@ Here's the definition of usePersistentInMemoryState:
```ts
export const usePersistentInMemoryState = <T>(initialValue: T, key: string) => {
const [state, setState] = useStateCheckEquality<T>(
safeJsonParse(store[key]) ?? initialValue
)
const [state, setState] = useStateCheckEquality<T>(safeJsonParse(store[key]) ?? initialValue)
useEffect(() => {
const storedValue = safeJsonParse(store[key]) ?? initialValue
@@ -192,7 +183,7 @@ In `use-bets`, we have this hook to get live updates with useApiSubscription:
```ts
export const useContractBets = (
contractId: string,
opts?: APIParams<'bets'> & { enabled?: boolean }
opts?: APIParams<'bets'> & {enabled?: boolean},
) => {
const {enabled = true, ...apiOptions} = {
contractId,
@@ -200,17 +191,11 @@ export const useContractBets = (
}
const optionsKey = JSON.stringify(apiOptions)
const [newBets, setNewBets] = usePersistentInMemoryState<Bet[]>(
[],
`${optionsKey}-bets`
)
const [newBets, setNewBets] = usePersistentInMemoryState<Bet[]>([], `${optionsKey}-bets`)
const addBets = (bets: Bet[]) => {
setNewBets((currentBets) => {
const uniqueBets = sortBy(
uniqBy([...currentBets, ...bets], 'id'),
'createdTime'
)
const uniqueBets = sortBy(uniqBy([...currentBets, ...bets], 'id'), 'createdTime')
return uniqueBets.filter((b) => !betShouldBeFiltered(b, apiOptions))
})
}
@@ -245,7 +230,7 @@ export function broadcastUpdatedPrivateUser(userId: string) {
broadcast(`private-user/${userId}`, {})
}
export function broadcastUpdatedUser(user: Partial<User> & { id: string }) {
export function broadcastUpdatedUser(user: Partial<User> & {id: string}) {
broadcast(`user/${user.id}`, {user})
}
@@ -330,7 +315,7 @@ export const placeBet: APIHandler<'bet'> = async (props, auth) => {
const isApi = auth.creds.kind === 'key'
return await betsQueue.enqueueFn(
() => placeBetMain(props, auth.uid, isApi),
[props.contractId, auth.uid]
[props.contractId, auth.uid],
)
}
```
@@ -376,13 +361,10 @@ using the pg-promise library. The client (code in web) does not have permission
Another example using the direct client:
```ts
export const getUniqueBettorIds = async (
contractId: string,
pg: SupabaseDirectClient
) => {
export const getUniqueBettorIds = async (contractId: string, pg: SupabaseDirectClient) => {
const res = await pg.manyOrNone(
'select distinct user_id from contract_bets where contract_id = $1',
[contractId]
[contractId],
)
return res.map((r) => r.user_id as string)
}
@@ -437,7 +419,7 @@ const query = renderSql(
from('contract_bets'),
where('contract_id = ${id}', {id}),
orderBy('created_time desc'),
limitValue != null && limit(limitValue)
limitValue != null && limit(limitValue),
)
const res = await pg.manyOrNone(query)
@@ -476,7 +458,7 @@ can do so via this SQL command (change the type if not `TEXT`):
```sql
ALTER TABLE profiles
ADD COLUMN profile_field TEXT;
ADD COLUMN profile_field TEXT;
```
Store it in `add_profile_field.sql` in the [migrations](../backend/supabase/migrations) folder and
@@ -540,28 +522,28 @@ How we apply it here
This project uses three complementary test types. Use the right level for the job:
- Unit tests
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Purpose: Verify a single function/module in isolation; fast, deterministic.
- Where: Each package under `tests/unit` (e.g., `backend/api/tests/unit`, `web/tests/unit`, `common/tests/unit`,
etc.).
- Runner: Jest (configured via root `jest.config.js`).
- Naming: `*.unit.test.ts` (or `.tsx` for React in `web`).
- When to use: Pure logic, utilities, hooks, reducers, small components with mocked dependencies.
- Integration tests
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
- Purpose: Verify multiple units working together (e.g., function + DB/client, component + context/provider) without
spinning up the full app.
- Where: Each package under `tests/integration` (e.g., `backend/shared/tests/integration`, `web/tests/integration`).
- Runner: Jest (configured via root `jest.config.js`).
- Naming: `*.integration.test.ts` (or `.tsx` for React in `web`).
- When to use: Boundaries between modules, real serialization/parsing, API handlers with mocked network/DB,
component trees with providers.
- End-to-End (E2E) tests
- Purpose: Validate real user flows across the full stack.
- Where: Top-level `tests/e2e` with separate areas for `web` and `backend`.
- Runner: Playwright (see root `playwright.config.ts`, `testDir: ./tests/e2e`).
- Naming: `*.e2e.spec.ts`.
- When to use: Critical journeys (signup, login, checkout), cross-service interactions, smoke tests for deployments.
- Purpose: Validate real user flows across the full stack.
- Where: Top-level `tests/e2e` with separate areas for `web` and `backend`.
- Runner: Playwright (see root `playwright.config.ts`, `testDir: ./tests/e2e`).
- Naming: `*.e2e.spec.ts`.
- When to use: Critical journeys (signup, login, checkout), cross-service interactions, smoke tests for deployments.
Quick commands
@@ -631,23 +613,23 @@ web/
- Unit and integration tests live in each packages `tests` folder and are executed by Jest via the root
`jest.config.js` projects array.
- Naming:
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`
- E2E (Playwright): `*.e2e.spec.ts`
- Unit: `*.unit.test.ts` (or `.tsx` for React in `web`)
- Integration: `*.integration.test.ts`
- E2E (Playwright): `*.e2e.spec.ts`
### Best Practices
* Test Behavior, Not Implementation. Dont test internal state or function calls unless youre testing utilities or very
- Test Behavior, Not Implementation. Dont test internal state or function calls unless youre testing utilities or very
critical behavior.
* Use msw to Mock APIs. Don't manually mock fetch—use msw to simulate realistic behavior, including network delays and
- Use msw to Mock APIs. Don't manually mock fetch—use msw to simulate realistic behavior, including network delays and
errors.
* Dont Overuse Snapshots. Snapshots are fragile and often meaningless unless used sparingly (e.g., for JSON response
- Dont Overuse Snapshots. Snapshots are fragile and often meaningless unless used sparingly (e.g., for JSON response
schemas).
* Prefer userEvent Over fireEvent. It simulates real user interactions more accurately.
* Avoid Testing Next.js Internals . You dont need to test getStaticProps, getServerSideProps themselves-test what they
- Prefer userEvent Over fireEvent. It simulates real user interactions more accurately.
- Avoid Testing Next.js Internals . You dont need to test getStaticProps, getServerSideProps themselves-test what they
render.
* Don't test just for coverage. Test to prevent regressions, document intent, and handle edge cases.
* Don't write end-to-end tests for features that change frequently unless absolutely necessary.
- Don't test just for coverage. Test to prevent regressions, document intent, and handle edge cases.
- Don't write end-to-end tests for features that change frequently unless absolutely necessary.
### Jest Unit Testing Guide
@@ -675,14 +657,14 @@ yarn test path/to/test.unit.test.ts
#### Test Standards
- Test file names should convey what to expect
- Follow the pattern: `<exact-filename>.[unit,integration].test.ts`. Examples:
- filename.unit.test.ts
- filename.integration.test.ts
- Follow the pattern: `<exact-filename>.[unit,integration].test.ts`. Examples:
- filename.unit.test.ts
- filename.integration.test.ts
- Group related tests using describe blocks
- Use descriptive test names that explain the expected behavior.
- Follow the pattern: "should `expected behavior` [relevant modifier]". Examples:
- should `ban user` [with matching user id]
- should `ban user` [with matching user name]
- Follow the pattern: "should `expected behavior` [relevant modifier]". Examples:
- should `ban user` [with matching user id]
- should `ban user` [with matching user name]
#### Mocking
@@ -726,15 +708,15 @@ When writing mocks, assert both outcome and interaction:
Why mocking is important?
- *Isolation* - Test your code independently of databases, APIs, and external systems. Tests only fail when your code
- _Isolation_ - Test your code independently of databases, APIs, and external systems. Tests only fail when your code
breaks, not when a server is down.
- *Speed* - Mocked tests run in milliseconds vs. seconds for real network/database calls. Run your suite constantly
- _Speed_ - Mocked tests run in milliseconds vs. seconds for real network/database calls. Run your suite constantly
without waiting.
- *Control* - Easily simulate edge cases like API errors, timeouts, or rare conditions that are difficult to reproduce
- _Control_ - Easily simulate edge cases like API errors, timeouts, or rare conditions that are difficult to reproduce
with real systems.
- *Reliability* - Eliminate unpredictable failures from network issues, rate limits, or changing external data. Same
- _Reliability_ - Eliminate unpredictable failures from network issues, rate limits, or changing external data. Same
inputs = same results, every time.
- *Focus* - Verify your function's logic and how it uses its dependencies, without requiring those dependencies to
- _Focus_ - Verify your function's logic and how it uses its dependencies, without requiring those dependencies to
actually work yet.
###### Use `jest.mock()`
@@ -747,40 +729,40 @@ functions return value isnt used, theres no need to mock it further.
```tsx
//Function and module mocks
jest.mock('path/to/module');
jest.mock('path/to/module')
//Function and module imports
import {functionUnderTest} from "path/to/function"
import {module} from "path/to/module"
import {functionUnderTest} from 'path/to/function'
import {module} from 'path/to/module'
describe('functionUnderTest', () => {
//Setup
beforeEach(() => {
//Run before each test
jest.resetAllMocks(); // Resets any mocks from previous tests
});
jest.resetAllMocks() // Resets any mocks from previous tests
})
afterEach(() => {
//Run after each test
jest.restoreAllMocks(); // Cleans up between tests
});
jest.restoreAllMocks() // Cleans up between tests
})
describe('when given valid input', () => {
it('should describe what is being tested', async () => {
//Arrange: Setup test data
const mockData = 'test';
const mockData = 'test'
//Act: Execute the function under test
const result = myFunction(mockData);
const result = myFunction(mockData)
//Assert: Verify the result
expect(result).toBe('expected');
});
});
expect(result).toBe('expected')
})
})
describe('when an error occurs', () => {
//Test cases for errors
});
});
})
})
```
###### Modules
@@ -790,27 +772,27 @@ called and what it was called with.
```tsx
//functionFile.ts
import {module as mockedDep} from "path/to/module"
import {module as mockedDep} from 'path/to/module'
export const functionUnderTest = async (param) => {
return await mockedDep(param);
};
return await mockedDep(param)
}
```
```tsx
//testFile.unit.test.ts
import {functionUnderTest} from "path/to/function";
import {module as mockedDep} from "path/to/module";
import {functionUnderTest} from 'path/to/function'
import {module as mockedDep} from 'path/to/module'
jest.mock('path/to/module');
jest.mock('path/to/module')
/**
* Inside the test case
* We create a mock for any information passed into the function that is being tested
* and if the function returns a result we create a mock to test the result
*/
const mockParam = "mockParam";
const mockReturnValue = "mockModuleValue";
const mockParam = 'mockParam'
const mockReturnValue = 'mockModuleValue'
/**
* use .mockResolvedValue when handling async/await modules that return values
@@ -818,15 +800,15 @@ const mockReturnValue = "mockModuleValue";
*/
describe('functionUnderTest', () => {
it('returns mocked module value and calls dependency correctly', async () => {
(mockedDep as jest.Mock).mockResolvedValue(mockReturnValue);
;(mockedDep as jest.Mock).mockResolvedValue(mockReturnValue)
const result = await functionUnderTest(mockParam);
const result = await functionUnderTest(mockParam)
expect(result).toBe(mockReturnValue);
expect(mockedDep).toHaveBeenCalledTimes(1);
expect(mockedDep).toHaveBeenCalledWith(mockParam);
});
});
expect(result).toBe(mockReturnValue)
expect(mockedDep).toHaveBeenCalledTimes(1)
expect(mockedDep).toHaveBeenCalledWith(mockParam)
})
})
```
Use namespace imports when you want to import everything a module exports under a single name.
@@ -834,37 +816,36 @@ Use namespace imports when you want to import everything a module exports under
```tsx
//moduleFile.ts
export const module = async (param) => {
const value = "module"
const value = 'module'
return value
};
}
export const moduleTwo = async (param) => {
const value = "moduleTwo"
const value = 'moduleTwo'
return value
};
}
```
```tsx
//functionFile.ts
import {module, moduleTwo} from "path/to/module"
import {module, moduleTwo} from 'path/to/module'
export const functionUnderTest = async (param) => {
const mockValue = await moduleTwo(param)
const returnValue = await module(mockValue)
return returnValue;
};
return returnValue
}
```
```tsx
//testFile.unit.test.ts
jest.mock('path/to/module');
jest.mock('path/to/module')
/**
* This creates an object containing all named exports from ./path/to/module
*/
import * as mockModule from "path/to/module"
(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue);
import * as mockModule from 'path/to/module'
;(mockModule.module as jest.Mock).mockResolvedValue(mockReturnValue)
```
When mocking modules, you can use `jest.spyOn()` instead of `jest.mock()`.
@@ -872,21 +853,21 @@ When mocking modules, you can use `jest.spyOn()` instead of `jest.mock()`.
- `jest.mock()` mocks the entire module, which is ideal for external dependencies like Axios or database clients.
- `jest.spyOn()` mocks specific methods while keeping the real implementation for others. It can also be used to observe
how a real method is called without changing its behavior.
- also replaces the need to have `jest.mock()` at the top of the file.
- also replaces the need to have `jest.mock()` at the top of the file.
```tsx
//testFile.unit.test.ts
import * as mockModule from "path/to/module"
import * as mockModule from 'path/to/module'
//Mocking the return value of the module
jest.spyOn(mockModule, 'module').mockResolvedValue(mockReturnValue);
jest.spyOn(mockModule, 'module').mockResolvedValue(mockReturnValue)
//Spying on the module to check functionality
jest.spyOn(mockModule, 'module');
jest.spyOn(mockModule, 'module')
//You can assert the module functionality with both of the above exactly like you would if you used jest.mock()
expect(mockModule.module).toBeCalledTimes(1);
expect(mockModule.module).toBeCalledWith(mockParam);
expect(mockModule.module).toBeCalledTimes(1)
expect(mockModule.module).toBeCalledWith(mockParam)
```
###### Dependencies
@@ -896,119 +877,114 @@ external functionality.
```tsx
//functionFile.ts
import {dependency} from "path/to/dependency"
import {dependency} from 'path/to/dependency'
export const functionUnderTest = async (param) => {
const depen = await dependency();
const value = depen.module();
const depen = await dependency()
const value = depen.module()
return value;
};
return value
}
```
```tsx
//testFile.unit.test.ts
jest.mock('path/to/dependency');
jest.mock('path/to/dependency')
import {dependency} from "path/to/dependency"
import {dependency} from 'path/to/dependency'
describe('functionUnderTest', () => {
/**
* Because the dependency has modules that are used we need to
* create a variable outside of scope that can be asserted on
*/
let mockDependency = {} as any;
let mockDependency = {} as any
beforeEach(() => {
mockDependency = {
module: jest.fn(),
};
jest.resetAllMocks(); // Resets any mocks from previous tests
});
}
jest.resetAllMocks() // Resets any mocks from previous tests
})
afterEach(() => {
//Run after each test
jest.restoreAllMocks(); // Cleans up between tests
});
jest.restoreAllMocks() // Cleans up between tests
})
//Inside the test case
(mockDependency.module as jest.Mock).mockResolvedValue(mockReturnValue);
;(mockDependency.module as jest.Mock).mockResolvedValue(mockReturnValue)
expect(mockDependency.module).toBeCalledTimes(1);
expect(mockDependency.module).toBeCalledWith(mockParam);
});
expect(mockDependency.module).toBeCalledTimes(1)
expect(mockDependency.module).toBeCalledWith(mockParam)
})
```
###### Error checking
```tsx
//function.ts
const result = await functionName(param);
const result = await functionName(param)
if (!result) {
throw new Error(403, 'Error text', error);
throw new Error(403, 'Error text', error)
}
;
```
```tsx
//testFile.unit.test.ts
const mockParam = {} as any;
const mockParam = {} as any
//This will check only the error message
expect(functionName(mockParam))
.rejects
.toThrowError('Error text');
expect(functionName(mockParam)).rejects.toThrowError('Error text')
//This will check the complete error
try {
await functionName(mockParam);
fail('Should have thrown');
await functionName(mockParam)
fail('Should have thrown')
} catch (error) {
const functionError = error as Error;
expect(functionError.code).toBe(403);
expect(functionError.message).toBe('Error text');
expect(functionError.details).toBe(mockParam);
expect(functionError.name).toBe('Error');
const functionError = error as Error
expect(functionError.code).toBe(403)
expect(functionError.message).toBe('Error text')
expect(functionError.details).toBe(mockParam)
expect(functionError.name).toBe('Error')
}
```
```tsx
//For console.error types
console.error('Error message', error);
console.error('Error message', error)
//Use spyOn to mock
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {
});
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
expect(errorSpy).toHaveBeenCalledWith(
'Error message',
expect.objectContaining({name: 'Error'}) //The error 'name' refers to the error type
);
expect.objectContaining({name: 'Error'}), //The error 'name' refers to the error type
)
```
###### Mocking array return value
```tsx
//arrayFile.ts
const exampleArray = [1, 2, 3, 4, 5];
const exampleArray = [1, 2, 3, 4, 5]
const arrayResult = exampleArray.includes(2);
const arrayResult = exampleArray.includes(2)
```
```tsx
//testFile.unit.test.ts
//This will mock 'includes' for all arrays and force the return value to be true
jest.spyOn(Array.prototype, 'includes').mockReturnValue(true);
jest.spyOn(Array.prototype, 'includes').mockReturnValue(true)
// ---
//This will specify which 'includes' array to mock based on the args passed into the .includes()
jest.spyOn(Array.prototype, 'includes').mockImplementation(function (value) {
if (value === 2) {
return true;
return true
}
return false;
});
return false
})
```
### Playwright (E2E) Testing Guide
@@ -1034,23 +1010,26 @@ yarn test:db:reset
Use this priority order for selecting elements in Playwright tests:
1. Prefer `getByRole()` — use semantic roles that reflect how users interact
```typescript
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByRole('button', {name: 'Submit'}).click()
```
If a meaningful ARIA role is not available, fall back to accessible text selectors (next point).
2. Use accessible text selectors — when roles don't apply, target user-facing text
```typescript
await page.getByLabel('Email').fill('user@example.com');
await page.getByPlaceholder('Enter your name').fill('John');
await page.getByText('Welcome back').isVisible();
await page.getByLabel('Email').fill('user@example.com')
await page.getByPlaceholder('Enter your name').fill('John')
await page.getByText('Welcome back').isVisible()
```
3. Only use `data-testid` — when elements have no stable user-facing text
```typescript
// For icons, toggles, or dynamic content without text
await page.getByTestId('menu-toggle').click();
await page.getByTestId('loading-spinner').isVisible();
await page.getByTestId('menu-toggle').click()
await page.getByTestId('loading-spinner').isVisible()
```
This hierarchy mirrors how users actually interact with your application, making tests more reliable and meaningful.

View File

@@ -1,10 +1,10 @@
name: CD Android Live Update
on:
push:
branches: [ main, master ]
branches: [main, master]
paths:
- "android/capawesome.json"
- ".github/workflows/cd-android-live-update.yml"
- 'android/capawesome.json'
- '.github/workflows/cd-android-live-update.yml'
jobs:
deploy:
@@ -15,7 +15,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # we need full history for git log
fetch-depth: 0 # we need full history for git log
- name: Install jq
run: sudo apt-get install -y jq

View File

@@ -1,10 +1,10 @@
name: CD API
on:
push:
branches: [ main, master ]
branches: [main, master]
paths:
- "backend/api/package.json"
- ".github/workflows/cd-api.yml"
- 'backend/api/package.json'
- '.github/workflows/cd-api.yml'
jobs:
deploy:
@@ -15,7 +15,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # we need full history for git log
fetch-depth: 0 # we need full history for git log
- name: Install jq
run: sudo apt-get install -y jq

View File

@@ -2,13 +2,12 @@ name: CD
# Must select "Read and write permissions" in GitHub → Repo → Settings → Actions → General → Workflow permissions
on:
push:
branches: [ main, master ]
branches: [main, master]
paths:
- "package.json"
- ".github/workflows/cd.yml"
- 'package.json'
- '.github/workflows/cd.yml'
jobs:
release:
@@ -18,7 +17,7 @@ jobs:
- name: Checkout repo
uses: actions/checkout@master
with:
fetch-depth: 0 # To fetch all history for tags
fetch-depth: 0 # To fetch all history for tags
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -32,4 +31,4 @@ jobs:
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
./scripts/release.sh
./scripts/release.sh

View File

@@ -2,9 +2,9 @@ name: E2E Tests
on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]
jobs:
e2e:
@@ -26,7 +26,7 @@ jobs:
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21' # Required for firebase-tools@15+
java-version: '21' # Required for firebase-tools@15+
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
@@ -41,8 +41,8 @@ jobs:
- name: Run E2E tests
env:
SKIP_DB_CLEANUP: true # Don't try to stop Docker in CI
FIREBASE_TOKEN: "dummy" # Suppresses auth warning
SKIP_DB_CLEANUP: true # Don't try to stop Docker in CI
FIREBASE_TOKEN: 'dummy' # Suppresses auth warning
# or
run: |
yarn test:e2e
@@ -61,4 +61,4 @@ jobs:
with:
name: test-results
path: test-results/
retention-days: 7
retention-days: 7

View File

@@ -2,9 +2,9 @@ name: Jest Tests
on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]
jobs:
ci:
@@ -29,22 +29,22 @@ jobs:
run: yarn lint
- name: Type check
run: npx tsc --noEmit
run: yarn typecheck
- name: Run Jest tests
env:
NEXT_PUBLIC_FIREBASE_ENV: DEV
run: |
yarn test:coverage
# npm install -g lcov-result-merger
# mkdir coverage
# lcov-result-merger \
# "backend/api/coverage/lcov.info" \
# "backend/shared/coverage/lcov.info" \
# "backend/email/coverage/lcov.info" \
# "common/coverage/lcov.info" \
# "web/coverage/lcov.info" \
# > coverage/lcov.info
# npm install -g lcov-result-merger
# mkdir coverage
# lcov-result-merger \
# "backend/api/coverage/lcov.info" \
# "backend/shared/coverage/lcov.info" \
# "backend/email/coverage/lcov.info" \
# "common/coverage/lcov.info" \
# "web/coverage/lcov.info" \
# > coverage/lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5