Skip to content

Commit

Permalink
test(v2): attribute e2e tests working
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevery committed Apr 2, 2024
1 parent 80d7525 commit 87f0b9e
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 41 deletions.
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-city/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@
}
],
"kind": "Function",
"content": "Returns the document head for the current page. The generic type describes the front matter.\n\n\n```typescript\nuseDocumentHead: <FrontMatter extends Record<string, unknown> = Record<string, any>>() => Required<Required<import(\"./types\").DocumentHeadValue<FrontMatter>>>\n```\n**Returns:**\n\nRequired&lt;Required&lt;import(\"./types\").[DocumentHeadValue](#documentheadvalue)<!-- -->&lt;FrontMatter&gt;&gt;&gt;",
"content": "Returns the document head for the current page. The generic type describes the front matter.\n\n\n```typescript\nuseDocumentHead: <FrontMatter extends Record<string, unknown> = Record<string, any>>() => Required<ResolvedDocumentHead<FrontMatter>>\n```\n**Returns:**\n\nRequired&lt;[ResolvedDocumentHead](#resolveddocumenthead)<!-- -->&lt;FrontMatter&gt;&gt;",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik-city/runtime/src/use-functions.ts",
"mdFile": "qwik-city.usedocumenthead.md"
},
Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/routes/api/qwik-city/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2328,12 +2328,12 @@ Returns the document head for the current page. The generic type describes the f
```typescript
useDocumentHead: <
FrontMatter extends Record<string, unknown> = Record<string, any>,
>() => Required<Required<import("./types").DocumentHeadValue<FrontMatter>>>;
>() => Required<ResolvedDocumentHead<FrontMatter>>;
```

**Returns:**

Required&lt;Required&lt;import("./types").[DocumentHeadValue](#documentheadvalue)&lt;FrontMatter&gt;&gt;&gt;
Required&lt;[ResolvedDocumentHead](#resolveddocumenthead)&lt;FrontMatter&gt;&gt;

[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik-city/runtime/src/use-functions.ts)

Expand Down
4 changes: 2 additions & 2 deletions packages/docs/src/routes/api/qwik/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1718,7 +1718,7 @@
}
],
"kind": "Function",
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<string>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; manifestHash?: string; manifestURL?: string; }\n\n\n</td><td>\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to `/build/`<!-- -->. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`<!-- -->. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").[JSXNode](#jsxnode)<!-- -->&lt;string&gt;",
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nLoad the prefetch graph for the container.\n\nEach Qwik container needs to include its own prefetch graph.\n\n\n```typescript\nPrefetchGraph: (opts?: {\n base?: string;\n manifestHash?: string;\n manifestURL?: string;\n}) => import(\"@builder.io/qwik\").JSXNode<string>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; manifestHash?: string; manifestURL?: string; }\n\n\n</td><td>\n\n_(Optional)_ Options for the loading prefetch graph.\n\n- `base` - Base of the graph. For a default installation this will default to `/build/`<!-- -->. But if more than one MFE is installed on the page, then each MFE needs to have its own base. - `manifestHash` - Hash of the manifest file to load. If not provided the hash will be extracted from the container attribute `q:manifest-hash` and assume the default build file `${base}/q-bundle-graph-${manifestHash}.json`<!-- -->. - `manifestURL` - URL of the manifest file to load if non-standard bundle graph location name.\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nimport(\"@builder.io/qwik\").[JSXNode](#jsxnode)<!-- -->&lt;string&gt;",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
"mdFile": "qwik.prefetchgraph.md"
},
Expand All @@ -1732,7 +1732,7 @@
}
],
"kind": "Function",
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n}) => import(\"@builder.io/qwik/jsx-runtime\").JSXNode<string>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; }\n\n\n</td><td>\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. - `path` - Path to the service worker.\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nimport(\"@builder.io/qwik/jsx-runtime\").[JSXNode](#jsxnode)<!-- -->&lt;string&gt;",
"content": "> This API is provided as an alpha preview for developers and may change based on feedback that we receive. Do not use this API in a production environment.\n> \n\nInstall a service worker which will prefetch the bundles.\n\nThere can only be one service worker per page. Because there can be many separate Qwik Containers on the page each container needs to load its prefetch graph using `PrefetchGraph` component.\n\n\n```typescript\nPrefetchServiceWorker: (opts: {\n base?: string;\n path?: string;\n verbose?: boolean;\n fetchBundleGraph?: boolean;\n}) => import(\"@builder.io/qwik\").JSXNode<string>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nopts\n\n\n</td><td>\n\n{ base?: string; path?: string; verbose?: boolean; fetchBundleGraph?: boolean; }\n\n\n</td><td>\n\nOptions for the prefetch service worker.\n\n- `base` - Base URL for the service worker. - `path` - Path to the service worker.\n\n\n</td></tr>\n</tbody></table>\n**Returns:**\n\nimport(\"@builder.io/qwik\").[JSXNode](#jsxnode)<!-- -->&lt;string&gt;",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts",
"mdFile": "qwik.prefetchserviceworker.md"
},
Expand Down
8 changes: 4 additions & 4 deletions packages/docs/src/routes/api/qwik/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3534,7 +3534,7 @@ PrefetchGraph: (opts?: {
base?: string;
manifestHash?: string;
manifestURL?: string;
}) => import("@builder.io/qwik/jsx-runtime").JSXNode<string>;
}) => import("@builder.io/qwik").JSXNode<string>;
```
<table><thead><tr><th>
Expand Down Expand Up @@ -3568,7 +3568,7 @@ _(Optional)_ Options for the loading prefetch graph.
</tbody></table>
**Returns:**
import("@builder.io/qwik/jsx-runtime").[JSXNode](#jsxnode)&lt;string&gt;
import("@builder.io/qwik").[JSXNode](#jsxnode)&lt;string&gt;
[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
Expand All @@ -3586,7 +3586,7 @@ PrefetchServiceWorker: (opts: {
path?: string;
verbose?: boolean;
fetchBundleGraph?: boolean;
}) => import("@builder.io/qwik/jsx-runtime").JSXNode<string>;
}) => import("@builder.io/qwik").JSXNode<string>;
```
<table><thead><tr><th>
Expand Down Expand Up @@ -3620,7 +3620,7 @@ Options for the prefetch service worker.
</tbody></table>
**Returns:**
import("@builder.io/qwik/jsx-runtime").[JSXNode](#jsxnode)&lt;string&gt;
import("@builder.io/qwik").[JSXNode](#jsxnode)&lt;string&gt;
[Edit this section](https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/core/components/prefetch.ts)
Expand Down
2 changes: 1 addition & 1 deletion packages/qwik-city/runtime/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ export type TypedDataValidator<T extends zod.ZodType = zod.ZodType> = {
export const useContent: () => ContentState;

// @public
export const useDocumentHead: <FrontMatter extends Record<string, unknown> = Record<string, any>>() => Required<Required<DocumentHeadValue<FrontMatter>>>;
export const useDocumentHead: <FrontMatter extends Record<string, unknown> = Record<string, any>>() => Required<ResolvedDocumentHead<FrontMatter>>;

// @public (undocumented)
export const useLocation: () => RouteLocation;
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
```ts

import * as CSS_2 from 'csstype';
import { JSXNode as JSXNode_2 } from '@builder.io/qwik/jsx-runtime';
import { JSXNode as JSXNode_2 } from '@builder.io/qwik';
import type { StreamWriter as StreamWriter_2 } from '@builder.io/qwik';

// @public
Expand Down Expand Up @@ -410,7 +410,7 @@ export { jsx }
export { jsx as jsxs }

// @internal (undocumented)
export const _jsxBranch: <T>(input?: T | undefined) => T | undefined;
export const _jsxBranch: <T>(input?: T) => T | undefined;

// @internal
export const _jsxC: <T extends string | FunctionComponent<any>>(type: T, varProps: Props | null, constProps: Props | null, flags: number, key: string | number | null, dev?: DevJSX) => JSXNode<T>;
Expand Down
2 changes: 2 additions & 0 deletions packages/qwik/src/core/components/prefetch.unit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { renderToString } from '../../server/render';
describe('PrefetchServiceWorker', () => {
describe('render', () => {
it('should render', async () => {
// eslint-disable-next-line no-console
await renderToString(<PrefetchServiceWorker />, { containerTagName: 'div' });
// eslint-disable-next-line no-console
});
});
});
52 changes: 35 additions & 17 deletions packages/qwik/src/core/v2/client/vnode-diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { isDev } from '@builder.io/qwik/build';
import { type OnRenderFn } from '../../component/component.public';
import { SERIALIZABLE_STATE } from '../../container/serializers';
import { assertDefined, assertFalse } from '../../error/assert';
import { _CONST_PROPS } from '../../internal';
import type { QRLInternal } from '../../qrl/qrl-class';
import type { QRL } from '../../qrl/qrl.public';
import { serializeAttribute } from '../../render/execute-component';
Expand Down Expand Up @@ -43,14 +42,14 @@ import {
ElementVNodeProps,
VNodeFlags,
VNodeProps,
VirtualVNodeProps,
type ClientAttrKey,
type ClientAttrs,
type ClientContainer,
type ElementVNode,
type TextVNode,
type VNode,
type VirtualVNode,
type ClientAttrs,
type ClientAttrKey,
VirtualVNodeProps,
} from './types';
import {
mapApp_findIndx,
Expand Down Expand Up @@ -164,7 +163,7 @@ export const vnode_diff = (container: ClientContainer, jsxNode: JSXOutput, vStar
if (Array.isArray(jsxValue)) {
descend(jsxValue, false);
} else if (isSignal(jsxValue)) {
expectVirtual(VirtualType.DerivedSignal);
expectVirtual(VirtualType.DerivedSignal, null);
descend(
trackSignal(jsxValue, [
SubscriptionType.TEXT_MUTABLE,
Expand All @@ -175,7 +174,7 @@ export const vnode_diff = (container: ClientContainer, jsxNode: JSXOutput, vStar
true
);
} else if (isPromise(jsxValue)) {
expectVirtual(VirtualType.Awaited);
expectVirtual(VirtualType.Awaited, null);
asyncQueue.push(jsxValue, vNewNode || vCurrent);
} else if (isJSXNode(jsxValue)) {
const type = jsxValue.type;
Expand All @@ -186,7 +185,7 @@ export const vnode_diff = (container: ClientContainer, jsxNode: JSXOutput, vStar
} else if (typeof type === 'function') {
if (type === Fragment) {
expectNoMoreTextNodes();
expectVirtual(VirtualType.Fragment);
expectVirtual(VirtualType.Fragment, jsxValue.key);
descend(jsxValue.children, true);
} else if (type === Slot) {
expectNoMoreTextNodes();
Expand Down Expand Up @@ -600,7 +599,7 @@ export const vnode_diff = (container: ClientContainer, jsxNode: JSXOutput, vStar
let returnValue = false;
qrls.flat(2).forEach((qrl) => {
if (qrl) {
const value = qrl(event) as any;
const value = qrl(event, element) as any;
returnValue = returnValue || value === true;
}
});
Expand Down Expand Up @@ -757,17 +756,36 @@ export const vnode_diff = (container: ClientContainer, jsxNode: JSXOutput, vStar
return vNodeWithKey;
}

function expectVirtual(type: VirtualType) {
if (vCurrent && vnode_isVirtualVNode(vCurrent)) {
function expectVirtual(type: VirtualType, jsxKey: string | null) {
if (
vCurrent &&
vnode_isVirtualVNode(vCurrent) &&
vnode_getProp(vCurrent, ELEMENT_KEY, null) === jsxKey
) {
// All is good.
} else {
vnode_insertBefore(
journal,
vParent as VirtualVNode,
(vNewNode = vnode_newVirtual()),
vCurrent && getInsertBefore()
);
return;
} else if (jsxKey !== null) {
// We have a key find it
vNewNode = retrieveChildWithKey(jsxKey);
if (vNewNode != null) {
// We found it, move it up.
vnode_insertBefore(
journal,
vParent as VirtualVNode,
(vNewNode = vnode_newVirtual()),
vCurrent && getInsertBefore()
);
return;
}
}
// Did not find it, insert a new one.
vnode_insertBefore(
journal,
vParent as VirtualVNode,
(vNewNode = vnode_newVirtual()),
vCurrent && getInsertBefore()
);
vnode_setProp(vNewNode as VirtualVNode, ELEMENT_KEY, jsxKey);
isDev && vnode_setProp((vNewNode || vCurrent) as VirtualVNode, DEBUG_TYPE, type);
}

Expand Down
42 changes: 39 additions & 3 deletions packages/qwik/src/core/v2/client/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,42 @@ export const vnode_journalToString = (journal: VNodeJournal): string => {
return lines.join('\n');
};

const parseBoolean = (value: string | boolean | null): boolean => {
if (value === 'false') {
return false;
}
return Boolean(value);
};

const isBooleanAttr = (element: Element, key: string): boolean => {
const isBoolean =
key == 'allowfullscreen' ||
key == 'async' ||
key == 'autofocus' ||
key == 'autoplay' ||
key == 'checked' ||
key == 'controls' ||
key == 'default' ||
key == 'defer' ||
key == 'disabled' ||
key == 'formnovalidate' ||
key == 'inert' ||
key == 'ismap' ||
key == 'itemscope' ||
key == 'loop' ||
key == 'multiple' ||
key == 'muted' ||
key == 'nomodule' ||
key == 'novalidate' ||
key == 'open' ||
key == 'playsinline' ||
key == 'readonly' ||
key == 'required' ||
key == 'reversed' ||
key == 'selected';
return isBoolean && key in element;
};

export const vnode_applyJournal = (journal: VNodeJournal) => {
// console.log('APPLY JOURNAL', vnode_journalToString(journal));
let idx = 0;
Expand All @@ -750,12 +786,12 @@ export const vnode_applyJournal = (journal: VNodeJournal) => {
key = 'class';
}
const value = journal[idx++] as string | null | boolean;
if (key === 'checked' && key in element) {
(element as HTMLInputElement).checked = value === 'true' || value === true;
if (isBooleanAttr(element, key)) {
(element as any)[key] = parseBoolean(value);
} else if (key === 'value' && key in element) {
(element as any).value = String(value);
} else {
if (value == null) {
if (value == null || value === false) {
element.removeAttribute(key);
} else {
element.setAttribute(key, String(value));
Expand Down
12 changes: 8 additions & 4 deletions packages/qwik/src/core/v2/ssr/ssr-render-jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { isDev } from '@builder.io/qwik/build';
import { isQwikComponent } from '../../component/component.public';
import { isQrl } from '../../qrl/qrl-class';
import type { QRL } from '../../qrl/qrl.public';
import { serializeAttribute } from '../../render/execute-component';
import { serializeAttribute } from '../../render/execute-component';
import { Fragment } from '../../render/jsx/jsx-runtime';
import { Slot } from '../../render/jsx/slot.public';
import type { JSXNode, JSXOutput } from '../../render/jsx/types/jsx-node';
import type { JSXChildren } from '../../render/jsx/types/jsx-qwik-attributes';
import type { JSXChildren } from '../../render/jsx/types/jsx-qwik-attributes';
import { SubscriptionType } from '../../state/common';
import { SignalDerived, isSignal } from '../../state/signal';
import { trackSignal } from '../../use/use-core';
Expand Down Expand Up @@ -139,7 +139,11 @@ function processJSXNode(
children != undefined && enqueue(children);
} else if (typeof type === 'function') {
if (type === Fragment) {
ssr.openFragment(isDev ? [DEBUG_TYPE, VirtualType.Fragment] : EMPTY_ARRAY);
let attrs = jsx.key != null ? [ELEMENT_KEY, jsx.key] : EMPTY_ARRAY;
if (isDev) {
attrs = [DEBUG_TYPE, VirtualType.Fragment, ...attrs]; // Add debug info.
}
ssr.openFragment(attrs);
enqueue(ssr.closeFragment);
// In theory we could get functions or regexes, but we assume all is well
const children = jsx.children as JSXOutput;
Expand Down Expand Up @@ -245,7 +249,7 @@ export function toSsrAttrs(
continue;
}

value = serializeAttribute(key, value);
value = serializeAttribute(key, value);

ssrAttrs.push(key, value as string);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/v2/vdom-diff.unit-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import { isStringifiable, type Stringifiable } from './shared-types';

import { format } from 'prettier';
import { createDocument } from '../../testing/document';
import { isElement } from '../../testing/html';
import { serializeBooleanOrNumberAttribute } from '../render/execute-component';
import type { JSXNode, JSXOutput } from '../render/jsx/types/jsx-node';
import { isText } from '../util/element';
import type { VirtualVNode } from './client/types';
import { isHtmlAttributeAnEventName, isJsxPropertyAnEventName } from './shared/event-names';
import { isElement } from '../../testing/html';

interface CustomMatchers<R = unknown> {
toMatchVDOM(expectedJSX: JSXOutput): R;
Expand Down Expand Up @@ -91,7 +91,7 @@ function diffJsxVNode(received: VNode, expected: JSXNode | string, path: string[
diffs.push(path.join(' > ') + ' expecting=' + expected.type + ' received=' + receivedTag);
}
const allProps: string[] = [];
expected.varProps && propsAdd(allProps, Object.keys(expected.varProps));
expected.varProps && propsAdd(allProps, Object.keys(expected.varProps));
expected.constProps && propsAdd(allProps, Object.keys(expected.constProps));
const receivedElement = vnode_isElementVNode(received)
? (vnode_getNode(received) as Element)
Expand Down
31 changes: 31 additions & 0 deletions starters/apps/e2e/src/components/attributes/attributes.e2e.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
domRender,
ssrRenderToDom,
} from "packages/qwik/src/core/v2/rendering.unit-util";
import { beforeEach, describe, it } from "vitest";
import { Attributes } from "./attributes";

const debug = true; //true;
Error.stackTraceLimit = 100;

describe.each([
{ render: ssrRenderToDom }, //
{ render: domRender }, //
])("$render.name: attributes.e2e", ({ render }) => {
describe("<Attributes/>", () => {
// let document: Document;
// let vNode: VNode;
// let container: ClientContainer;
// let getStyles: () => Record<string, string | string[]>;

beforeEach(async () => {
await render(<Attributes />, { debug });
// document = result.document;
// vNode = result.vNode!;
// container = result.container;
// getStyles = result.getStyles;
});

it("should run 'Toggle render'", async () => {});
});
});
Loading

0 comments on commit 87f0b9e

Please sign in to comment.