Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"insights",
"@builder.io/qwik-react",
"@builder.io/qwik-worker",
"qwik-cli-e2e"
"qwik-cli-e2e",
"qwik-react-test-app"
],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
Expand Down
5 changes: 5 additions & 0 deletions .changeset/lemon-hands-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik': patch
---

TEST: qwik react mount and unmount
5 changes: 5 additions & 0 deletions e2e/qwik-react-e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
playwright-report
dist
logs
server
tmp
15 changes: 15 additions & 0 deletions e2e/qwik-react-e2e/adapters/express/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { nodeServerAdapter } from '@builder.io/qwik-city/adapters/node-server/vite';
import { extendConfig } from '@builder.io/qwik-city/vite';
import baseConfig from '../../vite.config';

export default extendConfig(baseConfig, () => {
return {
build: {
ssr: true,
rollupOptions: {
input: ['src/entry.express.tsx', '@qwik-city-plan'],
},
},
plugins: [nodeServerAdapter({ name: 'express' })],
};
});
37 changes: 37 additions & 0 deletions e2e/qwik-react-e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "qwik-react-test-app",
"description": "Qwik react test app",
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"private": true,
"devDependencies": {
"@builder.io/qwik-react": "workspace:^",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"scripts": {
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.server": "vite build -c adapters/express/vite.config.ts",
"build.types": "tsc --incremental --noEmit",
"deploy": "vercel deploy",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"express": "pnpm build && pnpm serve",
"fmt": "prettier --write .",
"fmt.check": "prettier --check .",
"lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open",
"qwik": "qwik",
"serve": "node server/entry.express",
"start": "vite --open --mode ssr",
"test": "playwright test",
"test.debug": "playwright test --debug",
"test.ui": "playwright test --ui"
},
"type": "module"
}
48 changes: 48 additions & 0 deletions e2e/qwik-react-e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { defineConfig, devices } from '@playwright/test';

/** See https://playwright.dev/docs/test-configuration. */
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'line',

use: {
baseURL: 'http://localhost:3000',
// trace: 'on-first-retry',
// screenshot: 'only-on-failure',

// Increase timeouts for service worker operations
actionTimeout: 10000,
navigationTimeout: 10000,
},

// Increase global timeout for service worker tests
timeout: 30000,

projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],

webServer: {
command: 'npm run express',
port: 3000,
stdout: 'pipe',
reuseExistingServer: !process.env.CI,
},
});
28 changes: 28 additions & 0 deletions e2e/qwik-react-e2e/src/components/counter/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/** @jsxImportSource react */
import { useEffect, useState } from 'react';
import { qwikify$ } from '@builder.io/qwik-react';

interface IProps {
onMount(): void;
onUnmount(): void;
}

function Counter({ onMount, onUnmount }: IProps) {
const [count, setCount] = useState(0);

useEffect(() => {
onMount();
return () => onUnmount();
}, []);

return (
<div data-testid="test-component">
<span data-testid="count">count {count}</span>
<button data-testid="inc-btn" onClick={() => setCount((v) => v + 1)}>
inc
</button>
</div>
);
}

export const QCounter = qwikify$(Counter, { eagerness: 'hover' });
24 changes: 24 additions & 0 deletions e2e/qwik-react-e2e/src/components/router-head/router-head.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { component$ } from '@builder.io/qwik';
import { useDocumentHead, useLocation } from '@builder.io/qwik-city';

export const RouterHead = component$(() => {
const head = useDocumentHead();
const loc = useLocation();

return (
<>
<title>{head.title}</title>

<link rel="canonical" href={loc.url.href} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

{head.meta.map((m) => (
<meta key={m.key} {...m} />
))}

{head.links.map((l) => (
<link key={l.key} {...l} />
))}
</>
);
});
17 changes: 17 additions & 0 deletions e2e/qwik-react-e2e/src/entry.dev.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* WHAT IS THIS FILE?
*
* Development entry point using only client-side modules:
* - Do not use this mode in production!
* - No SSR
* - No portion of the application is pre-rendered on the server.
* - All of the application is running eagerly in the browser.
* - More code is transferred to the browser than in SSR mode.
* - Optimizer/Serialization/Deserialization code is not exercised!
*/
import { render, type RenderOptions } from '@builder.io/qwik';
import Root from './root';

export default function (opts: RenderOptions) {
return render(document, <Root />, opts);
}
67 changes: 67 additions & 0 deletions e2e/qwik-react-e2e/src/entry.express.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* WHAT IS THIS FILE?
*
* It's the entry point for the Express HTTP server when building for production.
*
* Learn more about Node.js server integrations here:
* - https://qwik.dev/docs/deployments/node/
*
*/
import { createQwikCity, type PlatformNode } from '@builder.io/qwik-city/middleware/node';
import 'dotenv/config';
import qwikCityPlan from '@qwik-city-plan';
import { manifest } from '@qwik-client-manifest';
import render from './entry.ssr';
import express from 'express';
import { fileURLToPath } from 'node:url';
import { join } from 'node:path';

declare global {
type QwikCityPlatform = PlatformNode;
}

// Directories where the static assets are located
const distDir = join(fileURLToPath(import.meta.url), '..', '..', 'dist');
const buildDir = join(distDir, 'build');

// Allow for dynamic port
const PORT = process.env.PORT ?? 3000;

// Create the Qwik City Node middleware
const { router, notFound } = createQwikCity({
render,
qwikCityPlan,
manifest,
// getOrigin(req) {
// // If deploying under a proxy, you may need to build the origin from the request headers
// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto
// const protocol = req.headers["x-forwarded-proto"] ?? "http";
// // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host
// const host = req.headers["x-forwarded-host"] ?? req.headers.host;
// return `${protocol}://${host}`;
// }
});

// Create the express server
// https://expressjs.com/
const app = express();

// Enable gzip compression
// app.use(compression());

// Static asset handlers
// https://expressjs.com/en/starter/static-files.html
app.use(`/build`, express.static(buildDir, { immutable: true, maxAge: '1y' }));
app.use(express.static(distDir, { redirect: false }));

// Use Qwik City's page and endpoint request handler
app.use(router);

// Use Qwik City's 404 handler
app.use(notFound);

// Start the express server
app.listen(PORT, () => {
/* eslint-disable */
console.log(`Server started: http://localhost:${PORT}/`);
});
19 changes: 19 additions & 0 deletions e2e/qwik-react-e2e/src/entry.preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* WHAT IS THIS FILE?
*
* It's the bundle entry point for `npm run preview`.
* That is, serving your app built in production mode.
*
* Feel free to modify this file, but don't remove it!
*
* Learn more about Vite's preview command:
* - https://vitejs.dev/config/preview-options.html#preview-options
*
*/
import { createQwikCity } from '@builder.io/qwik-city/middleware/node';
import qwikCityPlan from '@qwik-city-plan';
// make sure qwikCityPlan is imported before entry
import render from './entry.ssr';

/** The default export is the QwikCity adapter used by Vite preview. */
export default createQwikCity({ render, qwikCityPlan });
35 changes: 35 additions & 0 deletions e2e/qwik-react-e2e/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* WHAT IS THIS FILE?
*
* SSR entry point, in all cases the application is rendered outside the browser, this entry point
* will be the common one.
*
* - Server (express, cloudflare...)
* - Npm run start
* - Npm run preview
* - Npm run build
*/
import { renderToStream, type RenderToStreamOptions } from '@builder.io/qwik/server';
import { manifest } from '@qwik-client-manifest';
import Root from './root';

export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, {
manifest,
...opts,
// Use container attributes to set attributes on the html tag.
containerAttributes: {
lang: 'en-us',
...opts.containerAttributes,
},
// prefetchStrategy: {
// implementation: {
// linkInsert: "html-append",
// linkRel: "modulepreload",
// },
// },
serverData: {
...opts.serverData,
},
});
}
27 changes: 27 additions & 0 deletions e2e/qwik-react-e2e/src/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { component$ } from '@builder.io/qwik';
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city';
import { RouterHead } from './components/router-head/router-head';

export default component$(() => {
/**
* The root of a QwikCity site always start with the <QwikCityProvider> component, immediately
* followed by the document's <head> and <body>.
*
* Don't remove the `<head>` and `<body>` elements.
*/

return (
<QwikCityProvider>
<head>
<meta charSet="utf-8" />
<RouterHead />
{/* <PrefetchGraph />
<PrefetchServiceWorker /> */}
</head>
<body lang="en">
<RouterOutlet />
<ServiceWorkerRegister />
</body>
</QwikCityProvider>
);
});
16 changes: 16 additions & 0 deletions e2e/qwik-react-e2e/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { component$ } from '@builder.io/qwik';
import { type DocumentHead } from '@builder.io/qwik-city';

export default component$(() => {
return <>render qwik</>;
});

export const head: DocumentHead = {
title: 'Welcome to Qwik',
meta: [
{
name: 'description',
content: 'Qwik site description',
},
],
};
19 changes: 19 additions & 0 deletions e2e/qwik-react-e2e/src/routes/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { component$, Slot } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';

export default component$(() => {
return (
<>
<Link data-testid="qwik-link" href="/">
qwik
</Link>
<Link data-testid="react-link" href="/react">
react
</Link>
<br />
<main>
<Slot />
</main>
</>
);
});
Loading
Loading