mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-28 07:37:36 +09:00
## Description 1. What is this PR about (link the issue and add a short description) ## Steps for reproduction 1. click button 2. expect xyz ## Code Review - [ ] hi @kof, I need you to do - conceptual review (architecture, feature-correctness) - detailed review (read every line) - test it on preview ## Before requesting a review - [ ] made a self-review - [ ] added inline comments where things may be not obvious (the "why", not "what") ## Before merging - [ ] tested locally and on preview environment (preview dev login: 0000) - [ ] updated [test cases](https://github.com/webstudio-is/webstudio/blob/main/apps/builder/docs/test-cases.md) document - [ ] added tests - [ ] if any new env variables are added, added them to `.env` file
343 lines
7.7 KiB
TypeScript
343 lines
7.7 KiB
TypeScript
import { expect, test } from "vitest";
|
|
import { $, css, renderTemplate, ws } from "@webstudio-is/template";
|
|
import { generateFragmentFromHtml } from "./html";
|
|
|
|
test("generate instances from html", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<main>
|
|
<section>
|
|
<h1>It works!</h1>
|
|
<p>Webstudio is great.</p>
|
|
<ul>
|
|
<li>one</li>
|
|
<li>two</li>
|
|
</ul>
|
|
</section>
|
|
</main>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="main">
|
|
<ws.element ws:tag="section">
|
|
<ws.element ws:tag="h1">It works!</ws.element>
|
|
<ws.element ws:tag="p">Webstudio is great.</ws.element>
|
|
<ws.element ws:tag="ul">
|
|
<ws.element ws:tag="li">one</ws.element>
|
|
<ws.element ws:tag="li">two</ws.element>
|
|
</ws.element>
|
|
</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate multiple root instances from html", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<section>
|
|
<h1>One</h1>
|
|
</section>
|
|
<section>
|
|
<h1>Two</h1>
|
|
</section>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<>
|
|
<ws.element ws:tag="section">
|
|
<ws.element ws:tag="h1">One</ws.element>
|
|
</ws.element>
|
|
<ws.element ws:tag="section">
|
|
<ws.element ws:tag="h1">Two</ws.element>
|
|
</ws.element>
|
|
</>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("handle broken html", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<section>
|
|
<h1>One</h1>
|
|
</section attribute="value">
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<>
|
|
<ws.element ws:tag="section">
|
|
<ws.element ws:tag="h1">One</ws.element>
|
|
</ws.element>
|
|
</>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("handle non-html", () => {
|
|
expect(generateFragmentFromHtml("")).toEqual(renderTemplate(<></>));
|
|
expect(generateFragmentFromHtml("It works!")).toEqual(renderTemplate(<></>));
|
|
});
|
|
|
|
test("ignore custom elements", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<custom-element>
|
|
<div></div>
|
|
</custom-element>
|
|
<section></section>
|
|
`)
|
|
).toEqual(renderTemplate(<ws.element ws:tag="section"></ws.element>));
|
|
});
|
|
|
|
test("ignore not allowed tags", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`<div><marquee>test</marquee></div>`)
|
|
).toEqual(renderTemplate(<ws.element ws:tag="div"></ws.element>));
|
|
});
|
|
|
|
test("generate props from html attributes", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<form action="/my-action">
|
|
<button class="my-button">My Button</button>
|
|
</form>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="form" action="/my-action">
|
|
<ws.element ws:tag="button" class="my-button">
|
|
My Button
|
|
</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate props from number and boolean html attributes", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<button autofocus tabindex="-1">My Button</button>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="button" autofocus={true} tabindex={-1}>
|
|
My Button
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate props from number and boolean aria attributes", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<button aria-expanded aria-valuemin="100">My Button</button>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="button" aria-expanded={true} aria-valuemin={100}>
|
|
My Button
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("wrap text with span when spotted outside of rich text", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>div<article>article</article></div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<ws.element ws:tag="span">div</ws.element>
|
|
<ws.element ws:tag="article">article</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>div<b><br></b></div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<ws.element ws:tag="span">div</ws.element>
|
|
<ws.element ws:tag="b">
|
|
<ws.element ws:tag="br"></ws.element>
|
|
</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("do not wrap text with span when spotted near link", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>div<a>link</a></div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
div
|
|
<ws.element ws:tag="a">link</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("collapse any spacing characters inside text", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>
|
|
line
|
|
another line
|
|
</div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(<ws.element ws:tag="div">{"line another line"}</ws.element>)
|
|
);
|
|
});
|
|
|
|
test("collapse any spacing characters inside rich text", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>
|
|
<i> line </i>
|
|
<b> another line </b>
|
|
text
|
|
</div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<ws.element ws:tag="i">line</ws.element>{" "}
|
|
<ws.element ws:tag="b">another line</ws.element> text
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate style attribute as local styles", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div style="display: inline"></div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element
|
|
ws:tag="div"
|
|
ws:style={css`
|
|
display: inline;
|
|
`}
|
|
></ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("script as html embed", () => {
|
|
expect(generateFragmentFromHtml(`<script>a;</script>`)).toEqual(
|
|
renderTemplate(
|
|
<$.HtmlEmbed
|
|
ws:label="Script"
|
|
clientOnly={true}
|
|
code={`<script>a;</script>`}
|
|
/>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("style as html embed", () => {
|
|
expect(generateFragmentFromHtml(`<style>a;</style>`)).toEqual(
|
|
renderTemplate(<$.HtmlEmbed code={`<style>a;</style>`} ws:label="Style" />)
|
|
);
|
|
});
|
|
|
|
test("generate textarea element", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>
|
|
<textarea>
|
|
my text
|
|
</textarea>
|
|
</div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<ws.element ws:tag="textarea" value="my text" />
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate select element", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>
|
|
<select>
|
|
<option value="one">One</option>
|
|
<option value="two" selected>Two</option>
|
|
</select>
|
|
<select>
|
|
<option>One</option>
|
|
<option selected>Two</option>
|
|
</select>
|
|
</div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<ws.element ws:tag="select" value="two">
|
|
<ws.element ws:tag="option" value="one">
|
|
One
|
|
</ws.element>
|
|
<ws.element ws:tag="option" value="two">
|
|
Two
|
|
</ws.element>
|
|
</ws.element>
|
|
<ws.element ws:tag="select" value="Two">
|
|
<ws.element ws:tag="option">One</ws.element>
|
|
<ws.element ws:tag="option">Two</ws.element>
|
|
</ws.element>
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("generate Image component instead of img element", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<div>
|
|
<img src="./my-url">
|
|
</div>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<ws.element ws:tag="div">
|
|
<$.Image src="./my-url" />
|
|
</ws.element>
|
|
)
|
|
);
|
|
});
|
|
|
|
test("strip unsupported attribute names", () => {
|
|
expect(
|
|
generateFragmentFromHtml(`
|
|
<button @click="open = true">Expand</button>
|
|
<button x-on:click="open = !open">
|
|
Toggle
|
|
</button>
|
|
`)
|
|
).toEqual(
|
|
renderTemplate(
|
|
<>
|
|
<ws.element ws:tag="button">Expand</ws.element>
|
|
<ws.element ws:tag="button" x-on:click="open = !open">
|
|
Toggle
|
|
</ws.element>
|
|
</>
|
|
)
|
|
);
|
|
});
|