Skip to content

Commit 81543b8

Browse files
KemingHeweidependabot[bot]
authored
βœ… Playwright e2e tests (#404)
* πŸ”§ Moved build-only deps to dev-deps list moved tailwindcss, postcss, autoprefixer, and daisyui to dev deps list given they are strictly build-time. * πŸ“ Updated changeset with info on latest commit. * βœ… Added Playwright test to main user story, updated scripts Added accessibility-driven, automated Playwright test to verify main user story: enter GitHub repo, obtain preview URL, and see preview. Updated yarn testing scripts in package.json. Made repo.tsx more accessible. * πŸ“ Updated changeset following last playwright commit * ⬆️ Bump nanoid in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the / directory: [nanoid](https://github.com/ai/nanoid). Updates `nanoid` from 3.3.7 to 3.3.8 - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](ai/nanoid@3.3.7...3.3.8) --- updated-dependencies: - dependency-name: nanoid dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] <[email protected]> * βœ… Updated Playwright tests, config, and snapshots selectively checked out #405 's updated working 100% passing Playwright tests and config to current branch. Resovled to not including stargazers issue mentioned in #404 comments. Updated all snapshots to reflect this new config. Also only using Chrome and Mobile Chrome to ensure small testing footprint while covering responsiveness of the app. * πŸ“ Added contrib instructions and credits to devs in readme added detailed project setup, forking, local dev server, and testing and contributing instructions in readme. also added "community" section crediting all contributors to socialify so far. * πŸ“ Generated changeset to reflect latest commits. * πŸ‘· Configured Playwright for the build.yml CI script. Given that GITHUB_TOKEN is a reserved env var, testing if renaming the secret to SOCIALIFY_GITHUB_TOKEN and mapping it to the GITHUB_TOKEN env is a viable workaround. * πŸ‘· Reverted back to using default GITHUB_TOKEN to test fix. Previous commit of using a separate SOCIALIFY_GITHUB_TOKEN didn't work. Reverting back to using the default {{ secrets.GITHUB_TOKEN }} auto-assigned by GitHub Actions per run to test the fix. * πŸ’š Used step-scoped env rewrite to ensure GITHUB_TOKEN is valid for ci. GITHUB_TOKEN is reserved for GitHub Actions. Since Socialify uses the same named env, we must rewrite it only for the `yarn verify` step in the `build.yml` script. Requires repo-level actions secret ${{ secrets.SOCIALIFY_GITHUB_TOKEN }}. * πŸ’š Added PROJECT_URL env to ci script. Previous CI script might also be missing the PROJECT_URL env, added as a secret accessible at the same step level as the SOCIALIFY_GITHUB_TOKEN. * πŸ‘· Added upload playwright artifact to help debug behavior diff. * πŸ‘· Added continue-on-error: true to ci to ensure debugging artifact upload * πŸ‘· Reconfig ci Playwright artifact upload. * πŸ“ Clarified changeset requirements in README.md. * πŸ‘· Added --frozen-lockfile to yarn install in build.yml * πŸ“ Updated README.md contrib instr per owner's comments. * βœ… Specified font usage for Socialify to maintain Playwright consistency. * πŸ’„ Updated to using the Inter variable font for best prod UI match. * πŸ‘· Testing if fixed weight Inter improves CI Playwright * βœ… Added +1s to Playwright animation waiting to ensure consistency in CI. * βœ… Gave UI transition timeout a total of 5s to try improving consistency. Add multi-line, detailed description * βœ… Added 1% allowed buffer to UI test, kept user story test 100%. * βœ… Revised Playwright threshold: 99% for UI and 100% for output/user story Due to known GitHub Actions CI Playwright inconsistency issue not resolvable by waiting longer nor changing fonts, I propose 99% threshold for UI tests while keeping output/user story tests threshold at 100%. * πŸ“ Update README.md * πŸ’š Fix CI Build --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Wei He <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Wei He <[email protected]>
1 parent 545dc3a commit 81543b8

28 files changed

+499
-51
lines changed

β€Ž.changeset/warm-singers-pump.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"socialify": minor
3+
---
4+
5+
Added playwright e2e testing to capture user stories and main UI.
6+
7+
Updated contributor setup instructions and contributor list in README.md.

β€Ž.dockerignore

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22

3+
# Playwright results
4+
/.playwright/test-report/
5+
/.playwright/test-results/
6+
/.playwright/.cache/
7+
38
# dependencies
49
/node_modules
510
/.pnp

β€Ž.env.example

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# GitHub token
1+
# GitHub token with Public Repositories (read-only) access
2+
# Get one at https://github.com/settings/personal-access-tokens/new
23
GITHUB_TOKEN=
34
# API URL (Use public url in edge deployments, and localhost url in non-edge deployments)
45
PROJECT_URL=http://localhost:3000

β€Ž.github/workflows/build.yml

+19-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
name: Node.js CI
1+
name: Build
22

33
on:
4+
workflow_dispatch:
45
push:
56
pull_request:
6-
types: [assigned, opened, synchronize, reopened]
7+
types:
8+
- opened
9+
- synchronize
10+
- reopened
11+
schedule:
12+
- cron: '0 6 * * 0'
713

814
jobs:
915
build:
@@ -16,21 +22,16 @@ jobs:
1622
uses: actions/setup-node@v4
1723
with:
1824
node-version: 22.x
25+
cache: 'yarn'
1926

20-
- name: Get yarn cache directory path
21-
id: yarn-cache-dir-path
22-
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
27+
- name: Install dependencies
28+
run: yarn install --frozen-lockfile
29+
30+
- name: Lint
31+
run: yarn lint
2332

24-
- name: Cache yarn and next
25-
uses: actions/cache@v4
26-
with:
27-
path: |
28-
${{ steps.yarn-cache-dir-path.outputs.dir }}
29-
${{ github.workspace }}/.next/cache
30-
# Generate a new cache whenever packages or source files change.
31-
key: ${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
32-
# If source files changed but packages didn't, rebuild from a prior cache.
33-
restore-keys: |
34-
${{ runner.os }}-nextjs-${{ hashFiles('**/yarn.lock') }}-
35-
- run: yarn install
36-
- run: yarn verify
33+
- name: Unit Test
34+
run: yarn test:unit
35+
36+
- name: Build
37+
run: yarn build

β€Ž.github/workflows/e2e.yml

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: End-to-End Tests
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
pull_request_target:
7+
types:
8+
- opened
9+
- synchronize
10+
- reopened
11+
schedule:
12+
- cron: '0 6 * * 0'
13+
14+
jobs:
15+
e2e:
16+
runs-on: ubuntu-latest
17+
env:
18+
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/../.ms-playwright
19+
steps:
20+
- name: Checkout repo
21+
uses: actions/checkout@v4
22+
23+
- name: Setup node
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: 22.x
27+
cache: 'yarn'
28+
29+
- name: Install dependencies
30+
run: yarn install --frozen-lockfile
31+
32+
- name: Get Playwright's Version
33+
shell: bash # necessary for it to work on Windows, which uses powershell by default
34+
run: echo "PLAYWRIGHT_VERSION=$(node -p 'require("@playwright/test/package.json").version')" >> $GITHUB_OUTPUT
35+
id: playwright-version
36+
37+
- name: Cache playwright binaries
38+
uses: actions/cache@v4
39+
id: playwright-cache
40+
with:
41+
path: |
42+
${{ env.PLAYWRIGHT_BROWSERS_PATH }}
43+
key: ${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.PLAYWRIGHT_VERSION }}
44+
45+
- name: Install Playwrigtht dependencies
46+
run: yarn playwright install --with-deps chromium
47+
if: steps.playwright-cache.outputs.cache-hit != 'true'
48+
49+
- name: E2E Test
50+
run: yarn test:e2e
51+
env:
52+
GITHUB_TOKEN: ${{ secrets.PAT }}
53+
PROJECT_URL: http://127.0.0.1:3000
54+
55+
- name: Upload Playwright artifacts on failure
56+
if: failure()
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: playwright-artifacts
60+
path: .playwright/**/*
61+
include-hidden-files: true
62+
if-no-files-found: error

β€Ž.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22

3+
# Local vscode
4+
.vscode
5+
6+
# Playwright
7+
/.playwright/.cache
8+
/.playwright/test-report
9+
/.playwright/test-results
10+
311
# dependencies
412
/node_modules
513
/.pnp
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { type Page, expect, test } from '@playwright/test'
2+
3+
const customPageLoadTimeout = { timeout: 30000 }
4+
5+
// As a known CI issue, allow max 1% deviation in pixel diff.
6+
const customDiffPixelRatio = { maxDiffPixelRatio: 0.01 }
7+
8+
// Testing constants.
9+
const repoPreviewURL: string =
10+
'/wei/socialify?language=1&owner=1&name=1&stargazers=1&theme=Light'
11+
12+
test.describe('Socialify UI:', () => {
13+
test('is consistent for landing page', async ({
14+
page,
15+
}: { page: Page }): Promise<void> => {
16+
await page.goto('/', customPageLoadTimeout)
17+
18+
// Wait for the page to load/hydrate completely.
19+
await page.waitForLoadState('networkidle', customPageLoadTimeout)
20+
await page.waitForTimeout(5000)
21+
22+
const image = await page.screenshot()
23+
expect(image).toMatchSnapshot(customDiffPixelRatio)
24+
})
25+
26+
test('is consistent for error (404) page', async ({
27+
page,
28+
}: { page: Page }): Promise<void> => {
29+
await page.goto('/404', customPageLoadTimeout)
30+
31+
// Wait for the page to load/hydrate completely.
32+
await page.waitForLoadState('networkidle', customPageLoadTimeout)
33+
34+
const image = await page.screenshot()
35+
expect(image).toMatchSnapshot(customDiffPixelRatio)
36+
})
37+
38+
test('is consistent for preview config page', async ({
39+
page,
40+
}: { page: Page }): Promise<void> => {
41+
await page.goto(repoPreviewURL, customPageLoadTimeout)
42+
43+
// Wait for the page to load/hydrate completely.
44+
await page.waitForLoadState('networkidle', customPageLoadTimeout)
45+
46+
// To maintain consistency, de-select the 'Stars' checkbox,
47+
// and selects the 'Description' checkbox.
48+
await page.click('input[name="stargazers"]')
49+
await page.click('input[name="description"]')
50+
51+
// Wait for the component transition/animation to finish completely.
52+
await page.waitForTimeout(1000)
53+
54+
const image = await page.screenshot()
55+
expect(image).toMatchSnapshot(customDiffPixelRatio)
56+
57+
// Also check the toaster UI consistency.
58+
await page.click('button:has-text("URL")')
59+
await page.waitForSelector('[role="alert"]', customPageLoadTimeout)
60+
61+
// Wait for the component transition/animation to finish completely.
62+
await page.waitForTimeout(1000)
63+
64+
const toastImage = await page.screenshot()
65+
expect(toastImage).toMatchSnapshot(customDiffPixelRatio)
66+
})
67+
})
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { type Page, expect, test } from '@playwright/test'
2+
3+
const customTimeout = { timeout: 30000 }
4+
5+
// Testing constants.
6+
const repo: string = 'wei/socialify'
7+
const expectedConfigURL: string =
8+
'/wei/socialify?language=1&owner=1&name=1&stargazers=1&theme=Light'
9+
const expectedImageURLRegExp: RegExp =
10+
/\/wei\/socialify\/image\?description=1&language=1&name=1&owner=1&theme=Light$/
11+
12+
async function getClipboardText(page: Page): Promise<string> {
13+
return await page.evaluate(async () => {
14+
return await navigator.clipboard.readText()
15+
})
16+
}
17+
18+
test.beforeEach(async ({ page }: { page: Page }): Promise<void> => {
19+
await page.goto('/', customTimeout)
20+
21+
// Wait for the page to load/hydrate completely.
22+
await page.waitForLoadState('networkidle', customTimeout)
23+
})
24+
25+
test.describe('A simple user story:', () => {
26+
test(`user can enter a GitHub repo ("username/repo"), click submit button, click "URL", and get the social preview image`, async ({
27+
page,
28+
}: { page: Page }): Promise<void> => {
29+
// Input and submit the repo following accessibility best practices.
30+
await page.fill('input[name="repo-input"]', repo)
31+
await page.click('button[type="submit"]')
32+
33+
// Wait for navigation to the preview config page.
34+
await page.waitForSelector('button:has-text("URL")', customTimeout)
35+
36+
// Wait for the page to load/hydrate completely.
37+
await page.waitForLoadState('networkidle', customTimeout)
38+
await expect(page).toHaveURL(expectedConfigURL)
39+
40+
// To maintain consistency, de-select the 'Stars' checkbox,
41+
// and selects the 'Description' checkbox.
42+
await page.click('input[name="stargazers"]')
43+
await page.click('input[name="description"]')
44+
45+
// Obtain the consistent preview image URL.
46+
await page.click('button:has-text("URL")')
47+
48+
// Compare the clipboard content to the expected image URL.
49+
// (Only check the end of the URL due to dynamic localhost port allocation.)
50+
const url: string = await getClipboardText(page)
51+
expect(url).toMatch(expectedImageURLRegExp)
52+
53+
// Visit the image URL and snapshot the image.
54+
await page.goto(url, customTimeout)
55+
56+
// Wait for the page to load/hydrate completely.
57+
await page.waitForLoadState('networkidle', customTimeout)
58+
59+
const image = await page.screenshot()
60+
expect(image).toMatchSnapshot()
61+
})
62+
})

β€ŽREADME.md

+65-13
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,67 @@ You can use cli tool [mheap/github-social-image](https://github.com/mheap/github
5454
</a>
5555
</p>
5656

57-
## Development
57+
## Contributing
5858

59-
- Create a GitHub token from `Settings > Developer settings > Personal access tokens`, you'll need it in when setting up environemnt variables.
60-
- You'll need the `repo` scope
61-
- Run the following commands to set up the Development server:
59+
Please read and agree to our [Contributor Covenant Code of Conduct](https://github.com/wei/socialify/blob/master/CONTRIBUTING.md) to get started.
6260

63-
```shell
64-
# Clone
65-
git clone https://github.com/wei/socialify.git && cd $_
61+
### Running Socialify Locally
6662

67-
# Set environment variables in .env
68-
cp .env.example .env
63+
Run the following commands to get Socialify running locally:
6964

70-
yarn install
71-
yarn build
72-
yarn dev
73-
```
65+
```shell
66+
# Clone your fork of wei/socialify if contributing
67+
git clone https://github.com/wei/socialify.git && cd $_
68+
69+
# Set environment variables in .env.
70+
cp .env.example .env
71+
72+
# Install dependencies
73+
yarn install
74+
75+
# Start local development server
76+
yarn dev
77+
```
78+
79+
### Testing and Committing
80+
81+
Socialify uses [`biomejs`](https://biomejs.dev/) as linter/formatter, [`Jest`](https://jestjs.io/) for unit testing, and [`Playwright`](https://playwright.dev/) for end-to-end testing.
82+
83+
Make sure to run and pass the linter, unit and end-to-end tests locally before committing your code. Please let us know in case you need to update the test snapshots. More in `"scripts"` section in your `package.json` file.
84+
85+
```shell
86+
# Run linter/formatter
87+
yarn lint # yarn lint:fix
88+
89+
# Fix linter/formatter errors
90+
# yarn lint:fix
91+
92+
# Run unit tests
93+
yarn test:unit
94+
95+
# Install Playwright dependencies (first-time)
96+
# yarn playwright install --with-deps chrome
97+
98+
# Run e2e tests
99+
yarn test:e2e
100+
```
101+
102+
**One** changeset file is required per each PR.
103+
104+
1. Run `yarn changeset` and select the appropriate senmantic versioning type (major, minor, patch) based on the scope of the change, together with an concise message.
105+
2. You will see a new markdown file with a silly name like `milk-honey-eggs.md` in the `.changeset` directory.
106+
3. Test, commit, and push your code **WITH** this changeset file when submitting a new PR.
107+
4. You can manually edit it to include more information. For example:
108+
109+
```markdown
110+
---
111+
"socialify": minor
112+
---
113+
114+
Added playwright e2e testing to capture user stories and main UI
115+
```
116+
117+
That's it! We are tremendously grateful for your contribution to Socialify! :heart:
74118

75119
## Authors
76120

@@ -79,6 +123,14 @@ You can use cli tool [mheap/github-social-image](https://github.com/mheap/github
79123

80124
_Part of [@MLH-Fellowship](https://github.com/MLH-Fellowship) Pod 1.0.6_
81125

126+
## Community
127+
128+
These are the heros who continue to make Socialify the best social preview generator for the community.
129+
130+
<a href="https://github.com/wei/socialify/graphs/contributors">
131+
<img src="https://contrib.rocks/image?repo=wei/socialify" />
132+
</a>
133+
82134
## License
83135

84136
- [MIT](https://wei.mit-license.org)

β€Žjest.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ const customJestConfig = {
2020
'<rootDir>/**/__tests__/**/*.{js,jsx,ts,tsx}',
2121
'<rootDir>/**/*.{spec,test}.{js,jsx,ts,tsx}',
2222
],
23+
testPathIgnorePatterns: [
24+
'/node_modules/',
25+
'/.next/',
26+
'/.vercel/',
27+
'/.playwright/',
28+
],
2329
moduleNameMapper: {
2430
'\\.(css|less)$': 'identity-obj-proxy',
2531
},

0 commit comments

Comments
Β (0)