Skip to content

Commit f0b05d5

Browse files
authored
Merge pull request #108 from techulus/develop
Display scheduled time for posts
2 parents c3bb57b + 13e2be6 commit f0b05d5

11 files changed

Lines changed: 258 additions & 373 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Page E2E Tests
2+
3+
on:
4+
workflow_dispatch:
5+
6+
jobs:
7+
test:
8+
name: Run Playwright Tests
9+
permissions:
10+
contents: read
11+
runs-on: ubuntu-latest
12+
timeout-minutes: 10
13+
14+
steps:
15+
- name: Checkout code
16+
uses: actions/checkout@v4
17+
18+
- name: Setup pnpm
19+
uses: pnpm/action-setup@v4
20+
with:
21+
version: 10
22+
23+
- name: Setup Node.js
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: "20"
27+
cache: "pnpm"
28+
29+
- name: Install dependencies
30+
run: pnpm install --frozen-lockfile
31+
32+
- name: Install Playwright Browsers
33+
run: pnpm --filter @changes-page/page exec playwright install --with-deps chromium
34+
35+
- name: Run Playwright tests
36+
run: pnpm --filter @changes-page/page test
37+
38+
- name: Upload screenshots
39+
uses: actions/upload-artifact@v4
40+
if: always()
41+
with:
42+
name: screenshots
43+
path: |
44+
apps/page/screenshot.jpg
45+
apps/page/latestPostResponse-screenshot.jpg
46+
retention-days: 7
47+
if-no-files-found: ignore

‎apps/page/.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
# testing
99
/coverage
10+
/test-results/
11+
/playwright-report/
12+
/playwright/.cache/
1013

1114
# next.js
1215
/.next/

‎apps/page/package.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
"dev": "next dev --port 3001",
77
"build": "mkdir -p public/v1 && minify widget/widget.js > public/v1/widget.js && next build",
88
"start": "next start",
9-
"lint": "next lint"
9+
"lint": "next lint",
10+
"test": "playwright test",
11+
"test:headed": "playwright test --headed"
1012
},
1113
"dependencies": {
1214
"@arcjet/next": "1.0.0-alpha.20",
@@ -50,6 +52,7 @@
5052
},
5153
"devDependencies": {
5254
"@next/bundle-analyzer": "^13.1.6",
55+
"@playwright/test": "^1.57.0",
5356
"@types/cors": "^2.8.13",
5457
"@types/node": "^17.0.41",
5558
"@types/nprogress": "^0.2.3",

‎apps/page/playwright.config.js‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { defineConfig, devices } = require("@playwright/test");
2+
3+
module.exports = defineConfig({
4+
testDir: "./tests",
5+
fullyParallel: true,
6+
forbidOnly: !!process.env.CI,
7+
retries: process.env.CI ? 2 : 0,
8+
workers: process.env.CI ? 1 : undefined,
9+
reporter: "html",
10+
use: {
11+
trace: "on-first-retry",
12+
screenshot: "only-on-failure",
13+
},
14+
projects: [
15+
{
16+
name: "chromium",
17+
use: { ...devices["Desktop Chrome"] },
18+
},
19+
],
20+
});

‎apps/page/tests/page.spec.js‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const { expect, test, request } = require("@playwright/test");
2+
3+
test("visit page and take screenshot", async ({ page }) => {
4+
const targetUrl = process.env.ENVIRONMENT_URL || "https://hey.changes.page";
5+
6+
const response = await page.goto(targetUrl);
7+
8+
expect(response.status()).toBeLessThan(400);
9+
10+
await page.screenshot({ path: "screenshot.jpg" });
11+
12+
const context = await request.newContext({
13+
baseURL: targetUrl,
14+
});
15+
16+
const latest = await context.get(`/latest.json`);
17+
expect(latest.ok()).toBeTruthy();
18+
19+
const latestPost = await latest.json();
20+
const latestPostResponse = await page.goto(latestPost.url);
21+
expect(latestPostResponse.status()).toBeLessThan(400);
22+
await page.screenshot({ path: "latestPostResponse-screenshot.jpg" });
23+
});
24+
25+
test("verify APIs", async () => {
26+
const targetUrl = process.env.ENVIRONMENT_URL || "https://hey.changes.page";
27+
28+
const context = await request.newContext({
29+
baseURL: targetUrl,
30+
});
31+
32+
const posts = await context.get(`/changes.json`);
33+
expect(posts.ok()).toBeTruthy();
34+
35+
const robots = await context.get(`/robots.txt`);
36+
expect(robots.ok()).toBeTruthy();
37+
38+
const sitemap = await context.get(`/sitemap.xml`);
39+
expect(sitemap.ok()).toBeTruthy();
40+
41+
const markdown = await context.get(`/changes.md`);
42+
expect(markdown.ok()).toBeTruthy();
43+
44+
const rss = await context.get(`/rss.xml`);
45+
expect(rss.ok()).toBeTruthy();
46+
47+
const atom = await context.get(`/atom.xml`);
48+
expect(atom.ok()).toBeTruthy();
49+
});

‎apps/web/components/marketing/features.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,15 @@ export default function Features() {
9393
<span className="underline decoration-yellow-500 text-white">
9494
open-source
9595
</span>{" "}
96-
solution revolutionizing{" "}
96+
platform for{" "}
9797
<span className="underline decoration-red-500 text-white">
98-
changelog
98+
changelogs
9999
</span>{" "}
100100
and{" "}
101101
<span className="underline decoration-blue-500 text-white">
102-
roadmap
103-
</span>{" "}
104-
management.
102+
roadmaps
103+
</span>
104+
, built for modern teams.
105105
</p>
106106
</div>
107107
</div>

‎apps/web/components/marketing/hero.tsx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,11 @@ export default function Hero({ stars = null }: { stars?: string | null }) {
112112
</p>
113113

114114
<p className="mt-6 text-lg leading-8 text-gray-300">
115-
Our open-source platform empowers you to publish changelog pages and
116-
interactive roadmaps. Share what you&apos;ve built and what&apos;s
117-
coming next. Notify users via email, gather feedback with voting,
118-
track analytics, and enjoy a host of additional features. Kickstart
119-
your page in just a few minutes!
115+
Our open-source platform empowers you to share what you&apos;ve
116+
built with changelogs and what&apos;s coming next with roadmaps.
117+
Notify users via email, gather feedback with voting, track
118+
analytics, and enjoy a host of additional features. Kickstart your
119+
page in just a few minutes!
120120
</p>
121121

122122
<div className="mt-10 flex items-center gap-x-6">

‎apps/web/components/post/post.tsx‎

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
PostType,
99
} from "@changes-page/supabase/types/page";
1010
import { PostDateTime, PostTypeBadge } from "@changes-page/ui";
11+
import { DateTime } from "@changes-page/utils";
1112
import { Menu } from "@headlessui/react";
1213
import { DotsVerticalIcon } from "@heroicons/react/solid";
1314
import classNames from "classnames";
@@ -16,14 +17,14 @@ import ReactMarkdown from "react-markdown";
1617
import rehypeRaw from "rehype-raw";
1718
import rehypeSanitize from "rehype-sanitize";
1819
import remarkGfm from "remark-gfm";
20+
import { createAuditLog } from "../../utils/auditLog";
1921
import usePageUrl from "../../utils/hooks/usePageUrl";
2022
import { httpGet } from "../../utils/http";
2123
import { useUserData } from "../../utils/useUser";
2224
import { notifyError } from "../core/toast.component";
2325
import ConfirmDeleteDialog from "../dialogs/confirm-delete-dialog.component";
2426
import PostOptions from "./post-options";
2527
import { PostStatusIcon } from "./post-status";
26-
import { createAuditLog } from "../../utils/auditLog";
2728

2829
const ReactionsCounter = ({ aggregate }: { aggregate: IReactions }) => {
2930
return (
@@ -216,14 +217,36 @@ export function Post({
216217
<span className="inline-flex flex-col md:flex-row text-sm md:space-x-2 space-y-2 md:space-y-0 whitespace-nowrap text-gray-500 dark:text-gray-400">
217218
<PostDateTime publishedAt={publishedAt} startWithFullDate />
218219

219-
<div className="flex items-center -mt-0.5">
220+
<div className="flex items-center -mt-0.5 flex-wrap gap-2">
220221
{(post?.tags ?? []).map((tag: PostType, idx) => (
221-
<div key={tag} className={classNames(idx ? "ml-2" : "")}>
222+
<div key={tag}>
222223
<PostTypeBadge type={tag} />
223224
</div>
224225
))}
225226
{settings?.pinned_post_id === post.id && (
226-
<PostTypeBadge type="pinned" className="ml-2" />
227+
<PostTypeBadge type="pinned" />
228+
)}
229+
{post.status !== PostStatus.published && (
230+
<div className={classNames(
231+
"flex lg:hidden items-center space-x-1.5",
232+
post.status === PostStatus.draft && "text-slate-700 dark:text-slate-500",
233+
post.status === PostStatus.publish_later && "text-orange-700 dark:text-orange-500"
234+
)}>
235+
<PostStatusIcon
236+
status={post.status}
237+
className="w-4 h-4"
238+
/>
239+
<div className="flex flex-col">
240+
<span className="text-xs font-medium">
241+
{PostStatusToLabel[post.status]}
242+
</span>
243+
{post.status === PostStatus.publish_later && post.publish_at && (
244+
<span className="text-xs">
245+
{DateTime.fromISO(post.publish_at).toNiceFormat()}
246+
</span>
247+
)}
248+
</div>
249+
</div>
227250
)}
228251
</div>
229252

@@ -253,22 +276,25 @@ export function Post({
253276
<aside className="hidden lg:block absolute top-4 -right-52 w-48">
254277
<h2 className="sr-only">Options</h2>
255278
<div className="space-y-5">
256-
<div className="flex items-center space-x-2">
257-
<span
258-
className={classNames(
259-
"text-sm font-medium text-green-700 dark:text-green-500",
260-
post.status === PostStatus.draft &&
261-
"text-slate-700 dark:text-slate-500",
262-
post.status === PostStatus.publish_later &&
263-
"text-orange-700 dark:text-orange-500"
264-
)}
265-
>
279+
<div className={classNames(
280+
"flex flex-col space-y-1 text-green-700 dark:text-green-500",
281+
post.status === PostStatus.draft &&
282+
"text-slate-700 dark:text-slate-500",
283+
post.status === PostStatus.publish_later &&
284+
"text-orange-700 dark:text-orange-500"
285+
)}>
286+
<span className="text-sm font-medium">
266287
<PostStatusIcon
267288
status={post.status}
268289
className="w-5 h-5 inline-block mr-1.5"
269290
/>
270291
{PostStatusToLabel[post.status]}
271292
</span>
293+
{post.status === PostStatus.publish_later && post.publish_at && (
294+
<span className="text-xs pl-[1.625rem]">
295+
{DateTime.fromISO(post.publish_at).toNiceFormat()}
296+
</span>
297+
)}
272298
</div>
273299

274300
<div className="flex items-center space-x-2">
@@ -328,8 +354,8 @@ export function Post({
328354
<ReactionsCounter aggregate={reactions} />
329355
) : null}
330356
</div>
331-
</div>
332-
</li>
357+
</div >
358+
</li >
333359
</>
334360
);
335361
}

0 commit comments

Comments
 (0)