Skip to content

Commit

Permalink
Merge pull request #1 from renardeinside/bugfix/add-tests
Browse files Browse the repository at this point in the history
Fix several bugs & add tests and CI
  • Loading branch information
renardeinside authored Nov 23, 2024
2 parents 911c147 + b8b9432 commit 71c75eb
Show file tree
Hide file tree
Showing 18 changed files with 945 additions and 672 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: CI

on:
push:
branches:
- '**'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v2
with:
node-version: "20"
cache: "yarn"

- name: Install dependencies
run: yarn install

- name: Lint
run: yarn lint

- name: Install Playwright Browsers
run: npx playwright install chromium

# note: we need to build before running tests because it's a chrome extension
- name: Test
run: |
yarn build
yarn test:e2e
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ dist-ssr
*.njsproj
*.sln
*.sw?

test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ prepare-publishing-assets:
release:
@echo "Releasing version $(version)..."
yarn bump $(version)
yarn build
yarn package
git add .
git commit -m "Release: $(version)"
git tag -a $(version) -m "Release: $(version)"
Expand Down
18 changes: 18 additions & 0 deletions e2e/example.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { test, expect } from '@playwright/test';

test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});

test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();

// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist', 'src/components/ui', 'src/components/theme-provider.tsx', 'src/hooks/use-toast.ts'] },
{ ignores: ['dist', 'src/components/ui', 'src/components/theme-provider.tsx', 'src/hooks/use-toast.ts', 'tests'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
Expand Down
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build --emptyOutDir && zip -r dist/strophe.zip dist",
"build": "tsc -b && vite build --emptyOutDir",
"package": "yarn build && && zip -r dist/strophe.zip dist",
"bump": "node scripts/version-bump.mjs",
"lint": "eslint .",
"preview": "vite preview"
"preview": "vite preview",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"dependencies": {
"@fontsource-variable/fira-code": "^5.1.0",
Expand All @@ -21,7 +24,9 @@
"@tailwindcss/typography": "^0.5.15",
"@tiptap/core": "^2.9.1",
"@tiptap/extension-bubble-menu": "^2.9.1",
"@tiptap/extension-code-block": "^2.10.2",
"@tiptap/extension-code-block-lowlight": "^2.9.1",
"@tiptap/extension-collaboration": "^2.10.2",
"@tiptap/extension-horizontal-rule": "^2.9.1",
"@tiptap/extension-image": "^2.9.1",
"@tiptap/extension-link": "^2.9.1",
Expand All @@ -34,19 +39,28 @@
"@tiptap/starter-kit": "^2.9.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"highlight.js": "^11.10.0",
"jotai": "^2.10.2",
"lowlight": "^3.1.0",
"lucide-react": "^0.459.0",
"next-themes": "^0.4.3",
"prosemirror-model": "^1.23.0",
"prosemirror-state": "^1.4.3",
"prosemirror-view": "^1.37.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"sonner": "^1.7.0",
"tailwind-merge": "^2.5.4",
"tailwindcss-animate": "^1.0.7",
"y-indexeddb": "^9.0.12",
"y-prosemirror": "^1.2.13",
"y-protocols": "^1.0.6",
"yjs": "^13.6.20",
"zod": "^3.23.8"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@playwright/test": "^1.49.0",
"@types/node": "^22.9.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
Expand All @@ -58,6 +72,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"lorem-ipsum": "^2.0.8",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.15",
"typescript": "~5.6.2",
Expand Down
79 changes: 79 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: 'tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'list',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},

// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },

// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
61 changes: 29 additions & 32 deletions src/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,49 @@
import { EditorContent, useEditor } from "@tiptap/react";
import { useAtom } from "jotai";
import { contentAtom, subscriptionEffect } from "@/lib/stores";

import { lazy } from "react";
import { toast } from "sonner";
import { lazy, useEffect } from "react";
import { loadExtensions } from "@/lib/extensions";
import { LoaderCircle } from "lucide-react";
import { useYDoc } from "@/hooks/use-doc";

const EditorMenu = lazy(() => import("./EditorMenu"));

const Editor = () => {
const [content, setContent] = useAtom(contentAtom);
const Loading = () => {
return (
<div className="flex flex-col space-y-2 justify-center h-96 items-center">
<LoaderCircle className="animate-spin h-8 w-8" />
<span>Loading</span>
</div>
);
};

const safeParse = (value: string | null) => {
try {
return value ? JSON.parse(value) : "";
} catch (e) {
toast.error(`Failed to parse content due to ${e}`);
return "";
}
};
const Editor = () => {
const { doc, loaded } = useYDoc();

const editor = useEditor({
extensions: loadExtensions(),
content: safeParse(content),
onUpdate: ({ editor }) => {
setContent(JSON.stringify(editor.getJSON()));
},
autofocus: true,
extensions: [...loadExtensions(doc)],
editorProps: {
attributes: {
class:
"!w-full prose !max-w-none dark:prose-invert sm:prose-sm md:prose-md focus:outline-none min-h-[90vh] autofocus",
"!w-full prose !max-w-none dark:prose-invert prose-md leading-tight focus:outline-none min-h-[90vh]",
},
},
});

subscriptionEffect("st-content", editor, (value) => {
editor && editor.commands.setContent(safeParse(value));
});

useEffect(() => {
if (loaded && editor) {
editor.commands.focus("end");
}
}, [loaded, editor]);

return (
<>
{editor && (
<div className="flex px-8 pt-4 justify-center">
<EditorContent editor={editor} className="w-4/5" />
<EditorMenu editor={editor} />
</div>
)}
{loaded ? (
editor && (
<div className="flex px-8 pt-4 justify-center">
<EditorContent editor={editor} className="w-4/5" />
<EditorMenu editor={editor} />
</div>
)
) :<Loading />}
</>
);
};
Expand Down
41 changes: 41 additions & 0 deletions src/hooks/use-doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect, useState } from "react";
import * as Y from "yjs";
import { IndexeddbPersistence } from "y-indexeddb";

export const useYDoc = () => {
const [doc] = useState(() => new Y.Doc());
const [loaded, setLoaded] = useState(false);

useEffect(() => {
const persistence = new IndexeddbPersistence("st-content", doc);
const channel = new BroadcastChannel("st-channel-sync");

// Listen for updates from other tabs
channel.onmessage = (event) => {
if (event.data && event.data.type === "sync-update") {
Y.applyUpdate(doc, event.data.update);
}
};

// Broadcast updates to other tabs
doc.on("update", (update) => {
channel.postMessage({
type: "sync-update",
update,
});
});

// Wait for data to be loaded
persistence.whenSynced.then(() => {
setLoaded(true);
});

// Cleanup on unmount
return () => {
channel.close();
doc.destroy();
};
}, [doc]);

return { doc, loaded };
};
11 changes: 9 additions & 2 deletions src/lib/extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import TaskList from "@tiptap/extension-task-list";
import TaskItem from "@tiptap/extension-task-item";
import Image from "@tiptap/extension-image";
import { nodePasteRule, type PasteRuleFinder } from "@tiptap/core";

export const loadExtensions = () => {
import * as Y from "yjs";
import Collaboration from "@tiptap/extension-collaboration";
export const loadExtensions = (
doc: Y.Doc
) => {
const ImageFinder: PasteRuleFinder = /data:image\//g;

const ImageExtended = Image.extend({
Expand Down Expand Up @@ -36,6 +39,7 @@ export const loadExtensions = () => {
const extensions = [
StarterKit.configure({
codeBlock: false,
history: false,
}),
CodeBlockLowlight.configure({
lowlight,
Expand All @@ -47,6 +51,9 @@ export const loadExtensions = () => {
nested: true,
}),
ImageExtended,
Collaboration.configure({
document: doc, // Configure Y.Doc for collaboration
})
];

return extensions;
Expand Down
Loading

0 comments on commit 71c75eb

Please sign in to comment.