diff --git a/README.md b/README.md index 900089383e..7798ed9929 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A [feature-rich](https://livecodes.io/docs/features/), open-source, **client-sid [![LiveCodes: npm version](https://img.shields.io/npm/v/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: npm downloads](https://img.shields.io/npm/dm/livecodes)](https://www.npmjs.com/package/livecodes) [![LiveCodes: jsdelivr downloads](https://data.jsdelivr.com/v1/package/npm/livecodes/badge?style=rounded)](https://www.jsdelivr.com/package/npm/livecodes) -[![LiveCodes: languages](https://img.shields.io/badge/languages-96-blue)](https://livecodes.io/docs/languages/) +[![LiveCodes: languages](https://img.shields.io/badge/languages-97-blue)](https://livecodes.io/docs/languages/) [![LiveCodes: docs](https://img.shields.io/badge/Documentation-575757?logo=gitbook&logoColor=white)](https://livecodes.io/docs/) [![LiveCodes: llms.txt](https://img.shields.io/badge/llms.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms.txt) [![LiveCodes: llms-full.txt](https://img.shields.io/badge/llms--full.txt-575757?logo=googledocs&logoColor=white)](https://livecodes.io/docs/llms-full.txt) diff --git a/docs/docs/languages/go-wasm.mdx b/docs/docs/languages/go-wasm.mdx new file mode 100644 index 0000000000..1f8c108758 --- /dev/null +++ b/docs/docs/languages/go-wasm.mdx @@ -0,0 +1,93 @@ +# Go (Wasm) + +[Go](https://go.dev/) (Golang), is an open-source, statically typed, and compiled programming language developed by Google. It is designed for simplicity, efficiency, and strong support for concurrency, making it well-suited for building scalable and high-performance applications. + +LiveCodes uses [Yaegi](https://github.com/traefik/yaegi), the Go interpreter (running on WebAssembly), to run Go in the browser. + +:::info Note + +LiveCodes also supports running Go using [GopherJS](https://github.com/gopherjs/gopherjs) which is a Go to JavaScript compiler. Read documentation [here](./go.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const params = { + 'go-wasm': 'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello, World!")\n}\n', + console: 'full', +}; + + + +## Usage + +LiveCodes runs Go in the browser, including the [standard library](https://pkg.go.dev/std). + + +### Communication with JavaScript + +The Go code runs in the context of the [result page](../features/result.mdx). +A few helper properties and methods are available in the browser global `livecodes.goWasm` object: + +- `livecodes.goWasm.input`: the initial standard input that is passed to the Go code. +- `livecodes.goWasm.loaded`: A promise that resolves when the Go environment is loaded. Any other helpers should be used after this promise resolves. +- `livecodes.goWasm.output`: the standard output. +- `livecodes.goWasm.error`: the standard error. +- `livecodes.goWasm.exitCode`: the exit code. +- `livecodes.goWasm.run`: a function that runs the Go code with new input. This function takes a string as input and returns a promise that resolves when the Go code is done running. The promise resolves with an object containing the `input`, `output`, `error`, and `exitCode` properties. + +See the [example below](#example-usage) for more details. + +## Language Info + +### Name + +`go-wasm` + +### Extensions + +`wasm.go`, `go-wasm`, `gowasm` + +### Editor + +`script` + +## Compiler + +[Yaegi](https://github.com/traefik/yaegi), compiled to WebAssembly ([yaegi-wasm](https://github.com/Muhammad-Ayman/yaegi-wasm)) + +### Version + +Yaegi v0.16.1, running go1.25.0 + +## Code Formatting + +Using [GopherJS](https://github.com/gopherjs/gopherjs) + +## Example Usage + +This example demonstrates standard library usage and JavaScript interoperability (also check the code in the HTML editor): + + + + +## Live Reload + +By default, new code changes are sent to the result page for re-evaluation without a full page reload, to avoid the need to reload the Go environment. + +This behavior can be disabled by adding the code comment `// __livecodes_reload__` to the code, which will force a full page reload. +This comment can be added in the [`hiddenContent` property of the editor](../configuration/configuration-object.mdx#markup) for embedded playgrounds. + +## Starter Template + +https://livecodes.io/?template=go-wasm + +## Links + +- [Go](https://go.dev/) +- [Go documentation](https://go.dev/doc/) +- [Go standard library](https://pkg.go.dev/std) +- [Yaegi](https://github.com/traefik/yaegi) +- [Go using GopherJS](./go.mdx) in LiveCodes diff --git a/docs/docs/languages/go.mdx b/docs/docs/languages/go.mdx index 2e71b7f967..e3a584e872 100644 --- a/docs/docs/languages/go.mdx +++ b/docs/docs/languages/go.mdx @@ -1,3 +1,72 @@ # Go (Golang) -TODO... +[Go](https://go.dev/) (Golang), is an open-source, statically typed, and compiled programming language developed by Google. It is designed for simplicity, efficiency, and strong support for concurrency, making it well-suited for building scalable and high-performance applications. + +LiveCodes uses [GopherJS](https://github.com/gopherjs/gopherjs) which is a Go to JavaScript compiler, to run Go in the browser. + +:::info Note + +LiveCodes also supports running Go using [Yaegi](https://github.com/traefik/yaegi), the Go interpreter (running on WebAssembly). Read documentation [here](./go-wasm.mdx). + +::: + +## Demo + +import LiveCodes from '../../src/components/LiveCodes.tsx'; + +export const params = { + 'go': 'package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello, World!")\n}\n', + console: 'full', +}; + + + +## Usage + +LiveCodes runs Go in the browser, including the [standard library](https://pkg.go.dev/std). + +JavaScript interoperability and DOM access is achieved using [package `js`](https://pkg.go.dev/syscall/js) (see the [example below](#example-usage)). + +## Language Info + +### Name + +`go` + +### Extensions + +`go`, `golang` + +### Editor + +`script` + +## Compiler + +[GopherJS](https://github.com/gopherjs/gopherjs), the Go to JavaScript compiler. + +### Version + +GopherJS v1.19.0 beta1, running Go 1.19.13 + +## Code Formatting + +Using [GopherJS](https://github.com/gopherjs/gopherjs) + +## Example Usage + +This example demonstrates standard library usage, JavaScript interoperability and DOM access: + + + +## Starter Template + +https://livecodes.io/?template=go + +## Links + +- [Go](https://go.dev/) +- [Go documentation](https://go.dev/doc/) +- [Go standard library](https://pkg.go.dev/std) +- [GopherJS](https://github.com/gopherjs/gopherjs) +- [Go using Yaegi](./go-wasm.mdx) in LiveCodes diff --git a/docs/src/components/LanguageSliders.tsx b/docs/src/components/LanguageSliders.tsx index e1daff5ac6..960c4933a6 100644 --- a/docs/src/components/LanguageSliders.tsx +++ b/docs/src/components/LanguageSliders.tsx @@ -83,6 +83,7 @@ export default function Sliders() { { name: 'ruby', title: 'Ruby' }, { name: 'ruby-wasm', title: 'Ruby (Wasm)' }, { name: 'go', title: 'Go' }, + { name: 'go-wasm', title: 'Go (Wasm)' }, { name: 'php', title: 'PHP' }, { name: 'php-wasm', title: 'PHP (Wasm)' }, { name: 'cpp', title: 'C++' }, diff --git a/docs/src/components/TemplateList.tsx b/docs/src/components/TemplateList.tsx index 126f377f72..a246d4d47a 100644 --- a/docs/src/components/TemplateList.tsx +++ b/docs/src/components/TemplateList.tsx @@ -42,6 +42,7 @@ const templates = [ { name: 'ruby', title: 'Ruby Starter', thumbnail: 'ruby.svg' }, { name: 'ruby-wasm', title: 'Ruby (Wasm) Starter', thumbnail: 'ruby.svg' }, { name: 'go', title: 'Go Starter', thumbnail: 'go.svg' }, + { name: 'go-wasm', title: 'Go (Wasm) Starter', thumbnail: 'go.svg' }, { name: 'php', title: 'PHP Starter', thumbnail: 'php.svg' }, { name: 'php-wasm', title: 'PHP (Wasm) Starter', thumbnail: 'php.svg' }, { name: 'cpp', title: 'C++ Starter', thumbnail: 'cpp.svg' }, diff --git a/e2e/specs/compilers.spec.ts b/e2e/specs/compilers.spec.ts index bba6112526..d18dd75263 100644 --- a/e2e/specs/compilers.spec.ts +++ b/e2e/specs/compilers.spec.ts @@ -1,1783 +1,1783 @@ -import { expect } from '@playwright/test'; -import { getLoadedApp, waitForEditorFocus } from '../helpers'; -import { test } from '../test-fixtures'; - -test.describe('Compiler Results', () => { - test('HTML/CSS/JS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.type('hello, '); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text="CSS"'); - await waitForEditorFocus(app); - await page.keyboard.type('body {color: blue;}'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="JavaScript"'); - await waitForEditorFocus(app); - await page.keyboard.type('document.body.innerHTML += "world!"'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('body'); - - expect(resultText).toContain('hello, world!'); - expect(await getResult().$eval('body', (e) => getComputedStyle(e).color)).toBe( - 'rgb(0, 0, 255)', - ); - }); - - test('Markdown', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Markdown'); - await waitForEditorFocus(app); - await app.page().keyboard.type('# Hi There'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hi There'); - }); - - test('MDX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=MDX'); - await waitForEditorFocus(app); - await app.page().keyboard.type(` -import {Hello} from './script'; - - -`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JSX'); - await waitForEditorFocus(app); - await app.page().keyboard.type(` -import React from 'react'; -export const Hello = (props) =>

Hello, {props.title}!

; -`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hello, World!'); - }); - - test('Astro', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Astro'); - await waitForEditorFocus(app); - await app.page().keyboard.type(`--- -const title = "World"; ---- - -

Hello, {title}!

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hello, World!'); - }); - - test('Pug', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Pug'); - await waitForEditorFocus(app); - await page.keyboard.type('h1 Hi There'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toBe('Hi There'); - }); - - test('Haml', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Haml"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Haml"'); - await waitForEditorFocus(app); - await page.keyboard.type('.content Hello, #{name}!'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('.content'); - - expect(resultText).toContain('Hello, Haml!'); - }); - - test('AsciiDoc', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=AsciiDoc'); - await waitForEditorFocus(app); - await page.keyboard.type('== Hello, World!'); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h2'); - - expect(resultText).toContain('Hello, World!'); - }); - - test('Mustache', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Mustache"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Mustache'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Mustache'); - }); - - test('Mustache dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Mustache' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Mustache'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Mustache'); - }); - - test('Handlebars', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Handlebars"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Handlebars'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Handlebars'); - }); - - test('Handlebars dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Handlebars' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Handlebars'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Handlebars'); - }); - - test('Nunjucks', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Nunjucks"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Nunjucks'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Nunjucks'); - }); - - test('Nunjucks dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Nunjucks' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Nunjucks'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Nunjucks'); - }); - - test('EJS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "EJS"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=EJS'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= name %>

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to EJS'); - }); - - test('EJS dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'EJS' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=EJS'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= name %>

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to EJS'); - }); - - test('Eta', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Eta"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Eta"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= it.name %>

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Eta'); - }); - - test('Eta dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Eta' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Eta"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to <%= it.name %>

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Eta'); - }); - - test('Liquid', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name":"liquid"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Liquid'); - await waitForEditorFocus(app); - await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); - - await waitForResultUpdate(); - const body = await getResult().$('body'); - - expect(await body?.innerHTML()).toContain('Welcome to Liquid'); - }); - - test('Liquid dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'liquid' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Liquid'); - await waitForEditorFocus(app); - await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const body = await getResult().$('body'); - - expect(await body?.innerHTML()).toContain('Welcome to Liquid'); - }); - - test('doT', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name":"doT"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=doT'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{=it.name}}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to doT'); - }); - - test('doT dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'doT' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=doT'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{=it.name}}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to doT'); - }); - - test('Twig', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Twig"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Twig'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Twig'); - }); - - test('Twig dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Twig' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Twig'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Twig'); - }); - - test('Vento', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "Vento"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Vento'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Vento'); - }); - - test('Vento dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'Vento' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=Vento'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to Vento'); - }); - - test('art-template', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "art-template"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=art-template'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to art-template'); - }); - - test('art-template dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'art-template' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=art-template'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to art-template'); - }); - - test('jinja', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"data":{"name": "jinja"}}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Jinja"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to jinja'); - }); - - test('jinja dynamic', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('[aria-label="Project"]'); - await app.click('text=Custom Settings'); - await waitForEditorFocus(app, '#custom-settings-editor'); - await page.keyboard.press('Control+A'); - await page.keyboard.press('Delete'); - await page.keyboard.type(`{"template":{"prerender": false}}`); - await app.click('button:has-text("Load"):visible'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=JavaScript'); - await waitForEditorFocus(app); - await page.keyboard.type(`window.livecodes.templateData = { name: 'jinja' };`); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text="Jinja"'); - await waitForEditorFocus(app); - await page.keyboard.type(`

Welcome to {{ name }}

`); - - await waitForResultUpdate(); - await app.waitForTimeout(3000); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain('Welcome to jinja'); - }); - - test('BBCode', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=BBCode'); - await waitForEditorFocus(app); - await app.page().keyboard.type('[quote]quoted text[/quote]'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('blockquote'); - - expect(resultText).toBe('quoted text'); - }); - - test('MJML', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 1)'); - await app.click('text=MJML'); - await waitForEditorFocus(app); - await page.keyboard.type(` - - - - - - Hello MJML! - - - - - -`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('table'); - - expect(resultText).toContain('Hello MJML!'); - }); - - test('SCSS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=SCSS'); - await waitForEditorFocus(app); - await page.keyboard.type( - `$font-stack: Helvetica, sans-serif; body { font: 100% $font-stack; }`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); - }); - - test('Sass', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Sass'); - await waitForEditorFocus(app); - await page.keyboard.type(`$font-stack: Helvetica, sans-serif\nbody\n font: 100% $font-stack`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); - }); - - test('Less', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Less'); - await waitForEditorFocus(app); - await page.keyboard.type(`@width: 10px; #header { width: @width; }`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('width: 10px;'); - }); - - test('Stylus', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Stylus'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`font-size = 14px\nbody\n font font-size Arial, sans-serif`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('font: 14px Arial, sans-serif;'); - }); - - test('Stylis', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Stylis'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - '[namespace] {\n div {\n display: flex;\n\n @media screen {\n color: blue;\n }\n }\n\n div {\n transform: translateZ(0);\n\n h1, h2 {\n color: red;\n }\n }\n}', - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain( - '[namespace] div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}@media screen{[namespace] div{color:blue;}}[namespace] div{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);}[namespace] div h1,[namespace] div h2{color:red;}', - ); - }); - - test('PostCSS/postcssImportUrl', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Import Url'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`@import "github-markdown-css";`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('.markdown-body'); - }); - - test('PostCSS/Autoprefixer', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Autoprefixer'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`.example { user-select: none; }`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('-webkit-user-select: none;'); - }); - - test('PostCSS/Preset Env', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Preset Env'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `:root { --mainColor: #12345678; --secondaryColor: lab(32.5 38.5 -47.6 / 90%); }`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('style'); - - expect(resultText).toContain('--mainColor: rgba(18,52,86,0.47059);'); - }); - - test('Babel', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Babel'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`[1, 2, 3].map(n => n + 1);`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `[1, 2, 3].map(function (n) { - return n + 1; -});`, - ); - }); - - test('Sucrase', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Sucrase'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`const Greet = (name: string) => <>Hello {name}!;`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `const Greet = (name) => React.createElement(React.Fragment, null, "Hello " , name, "!");`, - ); - }); - - test('TypeScript', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=TypeScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `type Fish = { swim: () => void }; -type Bird = { fly: () => void }; -declare function getSmallPet(): Fish | Bird; -// ---cut--- -function isFish(pet: Fish | Bird): pet is Fish { - return (pet as Fish).swim !== undefined; -}`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - `// ---cut--- -function isFish(pet) { - return pet.swim !== undefined; -}`, - ); - }); - - test('Flow', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Flow'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - 'function foo(x: ?number): string {if (x) { return x; } return "default string"; }', - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain( - 'function foo(x ) {if (x) { return x; } return "default string"; }', - ); - }); - - test('JSX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`
Loading...
`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="JSX"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -const Hello = (props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('TSX', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="TSX"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -interface Props { name: string } -const Hello = (props: Props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('React', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`
Loading...
`); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="React"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -const Hello = (props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('React (TSX)', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text="React (TSX)"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -interface Props { name: string } -const Hello = (props: Props) =>

Hello, {props.name}

-export default () => ; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, React`); - }); - - test('Vue', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Vue`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Vue JSX', async ({ page, getTestUrl }) => { - const sfc = ` - - -`; - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - await getResult().click('text=Click me'); - await getResult().click('text=Click me'); - await getResult().click('text=Click me'); - - const titleText = await getResult().innerText('h1'); - expect(titleText).toBe('Hello, Vue!'); - - const counterText = await getResult().innerText('text=You clicked'); - expect(counterText).toBe('You clicked 3 times.'); - }); - - test('Vue import', async ({ page, getTestUrl }) => { - const sfc = ` - - -`; - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - // css import - const headHTML = await getResult().innerHTML('head'); - expect(headHTML).toContain( - '', - ); - - // bare module import - const uuidText = await getResult().innerText('#uuid'); - expect(uuidText.length).toBeGreaterThan(10); - - // import vue component - await getResult().click(':nth-match(button, 1)'); - await getResult().click(':nth-match(button, 1)'); - await getResult().click(':nth-match(button, 1)'); - - // import vue component that has relative imports and fetches absolute url - const buttonText = await getResult().innerText(':nth-match(button, 1)'); - expect(buttonText).toBe('Count is: 3, double is: 6'); - }); - - test('Vue langs', async ({ page, getTestUrl }) => { - const sfc = ` - - - - -`; - - await page.goto( - getTestUrl({ - x: 'code/N4IgLglmA2CmIC4QFUB2kawCYAIAKATgPYBWsAxmCADQhawDO5BEADpEaoiDeAIYBzBogDaAXVp9KEAG6wAolihEC3Ji3a8AtnwIBrAK6tEoaH1QCDg+EgAWYLdF7lOYWOm4gAvrQZgAnnAmIGYWVgI2IOQMwrQu6O5USN6+zGxJpuaW1twyBvBxroncADxuWqxmbjihAgC8ADogWlhNAHwNBA2oODgAxDgAErDQ0ETUOMDAOKh8WrA4Xl4AhJ3dvSWsOAjkZjGNIAAkfoGwAHRuAB5g7QxE8zhXYCUA9KwdXaiv5ZV8bh-dNZfdTpGpZA5gBhNHAMWBgIwAno4eJ+GZzWAIGFgFgWHB1HAAcgAavkcABmAkAbiBrxB7ERNJOcDBFgOTBi0K0RCwBjgiN6hwAZq4ALR+KR6THDaBySDkPgTBjmBhi2AsQXUz4C1gsHQEfwilxjAiYvoAVgtmvWOGOFE4WF0BqNKkxAgIsHcVtQQN6tgAjJMfb0cML0JiAEwABkjAFIbaGwGKwBKvcHkURjZjDjqIHqnRmVKnFkCgxdYNdA1rgwnMX7o3GhaLxeQ9EXes6TTbYfEHfrDQWCEWvDSXkzYG0UiAxwxbB7IaIJFO0uxhAhxHEYoRGHDPLQdURyIw7gRV+uogY-PcAMpwyAWVfAHwgXOsFTzhCP2gBViMExPtx+A+IRZOEkTfowy5UIUCQeMkT5yCeECcNwAAs3hAA', - }), - ); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - // await page.keyboard.insertText(sfc); - - await waitForResultUpdate(); - - const headerText = await getResult().innerText('h1'); - - // markdown, scss, typescript - expect(headerText).toContain(`Hello, Vue 3!`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(85, 85, 85)'); - - // css modules - expect(await getResult().$eval('p', (e) => getComputedStyle(e).color)).toBe('rgb(0, 128, 0)'); - }); - - test('Vue + Tailwind CSS', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="vue"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` -`, - ); - - await app.click(':nth-match([title="Change Language"], 2)'); - await app.click('text=Tailwind CSS'); - await app.click('text=CSS'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `@tailwind base; -@tailwind components; -@tailwind utilities; -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Tailwind in Vue SFC`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe( - 'rgb(220, 38, 38)', - ); - }); - - test('Vue 2', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Vue 2'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Vue 2`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Svelte', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await (await app.$('[data-lang="svelte"]'))?.click(); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -
-

Hello, {title}

-
-`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Svelte`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Malina.js', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Malina.js'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - ` - -
-

Hello, {title}

-
-`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Malina.js`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('Stencil', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(''); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Stencil'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `import { Component, Prop, h } from "@stencil/core"; -@Component({ - tag: "my-app", - styles: "h1 { color: blue; }", -}) -export class App { - @Prop() title: string; - render() { - return ( -

Hello, {this.title}

- ); - } -}`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('h1'); - - expect(resultText).toContain(`Hello, Stencil`); - expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); - }); - - test('CoffeeScript', async ({ page, getTestUrl }) => { - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=CoffeeScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`square = (x) -> x * x`); - - await waitForResultUpdate(); - const resultText = await getResult().innerHTML('body > script'); - - expect(resultText).toContain(`var square;`); - expect(resultText).toContain(`square = function(x) { - return x * x; -};`); - }); - - test('LiveScript', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=LiveScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`{ capitalize, join, map, words } = require 'prelude-ls' -title = 'live script' -|> words -|> map capitalize -|> join '' -(document.getElementById \\title).textContent = title`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, LiveScript`); - }); - - test('Riot.js', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText(''); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Riot.js'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, {props.title}

'); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Riot.js`); - }); - - test('AssemblyScript', async ({ page, getTestUrl }) => { - test.fixme(); - - await page.goto(getTestUrl()); - - const { app, getResult } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText( - `

Hello, World

- `, - ); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=AssemblyScript'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`export function getTitle(): string {return "AssemblyScript";`); - // workaround for monaco auto-complete - await page.keyboard.press('Delete'); - await page.keyboard.insertText(`}`); - - await app.waitForTimeout(15000); - const resultText = await getResult().innerText('text=Hello, AssemblyScript'); - - expect(resultText).toContain(`Hello, AssemblyScript`); - }); - - test('Python', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Python'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`from browser import document -title = 'Python' -document['header'].html = f"Hello, {title}"`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Python`); - }); - - test('Pyodide', async ({ page, getTestUrl }) => { - test.skip(); - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, World

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Pyodide'); - await waitForEditorFocus(app); - await page.keyboard.insertText(`from js import document -title = 'Python' -document.getElementById('header').innerHTML = f"Hello, {title}"`); - - await waitForResultUpdate(); - await getResult().waitForSelector('text=Hello, Python'); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Python`); - }); - - test('Ruby', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Ruby'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`require "native" -title = 'Ruby' -$$.document.querySelector('#title').innerHTML = title`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('text=Hello, Ruby'); - - expect(resultText).toContain(`Hello, Ruby`); - }); - - test('Go', async ({ page, getTestUrl, editor }) => { - test.slow(); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Go'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`package main -import "syscall/js" -func main() { - js.Global().Get("document").Call("querySelector", "#title").Set("innerHTML", "Golang") -}`); - - await waitForResultUpdate({ delay: 4000, timeout: 60_000 }); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Golang`); - }); - - test('PHP', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=PHP'); - await waitForEditorFocus(app); - - // go below pre-inserted 'getElementById('title')->textContent = $title;`, - ); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, PHP`); - }); - - test('Perl', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Perl'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`use strict; -my $title = 'Perl'; -JS::inline('document.getElementById("title").innerHTML') = $title;`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Perl`); - }); - - test('Lua', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Lua'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`js = require "js" -window = js.global -document = window.document -document:getElementById("title").innerHTML = "Lua"`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Lua`); - }); - - test('Scheme', async ({ page, getTestUrl, editor }) => { - test.skip(editor === 'codejar', 'please fix'); - - await page.goto(getTestUrl()); - - const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); - - await app.click('text="HTML"'); - await waitForEditorFocus(app); - await page.keyboard.insertText('

Hello, world

'); - - await app.click(':nth-match([title="Change Language"], 3)'); - await app.click('text=Scheme'); - await waitForEditorFocus(app); - - await page.keyboard.insertText(`(let ((title "Scheme")) - (set-content! "#title" title))`); - - await waitForResultUpdate(); - const resultText = await getResult().innerText('h1'); - - expect(resultText).toContain(`Hello, Scheme`); - }); -}); +import { expect } from '@playwright/test'; +import { getLoadedApp, waitForEditorFocus } from '../helpers'; +import { test } from '../test-fixtures'; + +test.describe('Compiler Results', () => { + test('HTML/CSS/JS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.type('hello, '); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text="CSS"'); + await waitForEditorFocus(app); + await page.keyboard.type('body {color: blue;}'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JavaScript"'); + await waitForEditorFocus(app); + await page.keyboard.type('document.body.innerHTML += "world!"'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('body'); + + expect(resultText).toContain('hello, world!'); + expect(await getResult().$eval('body', (e) => getComputedStyle(e).color)).toBe( + 'rgb(0, 0, 255)', + ); + }); + + test('Markdown', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Markdown'); + await waitForEditorFocus(app); + await app.page().keyboard.type('# Hi There'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hi There'); + }); + + test('MDX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=MDX'); + await waitForEditorFocus(app); + await app.page().keyboard.type(` +import {Hello} from './script'; + + +`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JSX'); + await waitForEditorFocus(app); + await app.page().keyboard.type(` +import React from 'react'; +export const Hello = (props) =>

Hello, {props.title}!

; +`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hello, World!'); + }); + + test('Astro', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Astro'); + await waitForEditorFocus(app); + await app.page().keyboard.type(`--- +const title = "World"; +--- + +

Hello, {title}!

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hello, World!'); + }); + + test('Pug', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Pug'); + await waitForEditorFocus(app); + await page.keyboard.type('h1 Hi There'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toBe('Hi There'); + }); + + test('Haml', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Haml"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Haml"'); + await waitForEditorFocus(app); + await page.keyboard.type('.content Hello, #{name}!'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('.content'); + + expect(resultText).toContain('Hello, Haml!'); + }); + + test('AsciiDoc', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=AsciiDoc'); + await waitForEditorFocus(app); + await page.keyboard.type('== Hello, World!'); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h2'); + + expect(resultText).toContain('Hello, World!'); + }); + + test('Mustache', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Mustache"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Mustache'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Mustache'); + }); + + test('Mustache dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Mustache' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Mustache'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Mustache'); + }); + + test('Handlebars', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Handlebars"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Handlebars'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Handlebars'); + }); + + test('Handlebars dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Handlebars' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Handlebars'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Handlebars'); + }); + + test('Nunjucks', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Nunjucks"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Nunjucks'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Nunjucks'); + }); + + test('Nunjucks dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Nunjucks' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Nunjucks'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Nunjucks'); + }); + + test('EJS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "EJS"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=EJS'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= name %>

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to EJS'); + }); + + test('EJS dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'EJS' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=EJS'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= name %>

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to EJS'); + }); + + test('Eta', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Eta"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= it.name %>

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Eta'); + }); + + test('Eta dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Eta' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Eta"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to <%= it.name %>

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Eta'); + }); + + test('Liquid', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name":"liquid"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Liquid'); + await waitForEditorFocus(app); + await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); + + await waitForResultUpdate(); + const body = await getResult().$('body'); + + expect(await body?.innerHTML()).toContain('Welcome to Liquid'); + }); + + test('Liquid dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'liquid' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Liquid'); + await waitForEditorFocus(app); + await page.keyboard.type(`{{ name | capitalize | prepend: "Welcome to "}}`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const body = await getResult().$('body'); + + expect(await body?.innerHTML()).toContain('Welcome to Liquid'); + }); + + test('doT', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name":"doT"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=doT'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{=it.name}}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to doT'); + }); + + test('doT dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'doT' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=doT'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{=it.name}}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to doT'); + }); + + test('Twig', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Twig"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Twig'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Twig'); + }); + + test('Twig dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Twig' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Twig'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Twig'); + }); + + test('Vento', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "Vento"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Vento'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Vento'); + }); + + test('Vento dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'Vento' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=Vento'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to Vento'); + }); + + test('art-template', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "art-template"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=art-template'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to art-template'); + }); + + test('art-template dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'art-template' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=art-template'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to art-template'); + }); + + test('jinja', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"data":{"name": "jinja"}}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Jinja"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to jinja'); + }); + + test('jinja dynamic', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('[aria-label="Project"]'); + await app.click('text=Custom Settings'); + await waitForEditorFocus(app, '#custom-settings-editor'); + await page.keyboard.press('Control+A'); + await page.keyboard.press('Delete'); + await page.keyboard.type(`{"template":{"prerender": false}}`); + await app.click('button:has-text("Load"):visible'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=JavaScript'); + await waitForEditorFocus(app); + await page.keyboard.type(`window.livecodes.templateData = { name: 'jinja' };`); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text="Jinja"'); + await waitForEditorFocus(app); + await page.keyboard.type(`

Welcome to {{ name }}

`); + + await waitForResultUpdate(); + await app.waitForTimeout(3000); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain('Welcome to jinja'); + }); + + test('BBCode', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=BBCode'); + await waitForEditorFocus(app); + await app.page().keyboard.type('[quote]quoted text[/quote]'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('blockquote'); + + expect(resultText).toBe('quoted text'); + }); + + test('MJML', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 1)'); + await app.click('text=MJML'); + await waitForEditorFocus(app); + await page.keyboard.type(` + + + + + + Hello MJML! + + + + + +`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('table'); + + expect(resultText).toContain('Hello MJML!'); + }); + + test('SCSS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=SCSS'); + await waitForEditorFocus(app); + await page.keyboard.type( + `$font-stack: Helvetica, sans-serif; body { font: 100% $font-stack; }`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); + }); + + test('Sass', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Sass'); + await waitForEditorFocus(app); + await page.keyboard.type(`$font-stack: Helvetica, sans-serif\nbody\n font: 100% $font-stack`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 100% Helvetica, sans-serif;'); + }); + + test('Less', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Less'); + await waitForEditorFocus(app); + await page.keyboard.type(`@width: 10px; #header { width: @width; }`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('width: 10px;'); + }); + + test('Stylus', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Stylus'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`font-size = 14px\nbody\n font font-size Arial, sans-serif`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('font: 14px Arial, sans-serif;'); + }); + + test('Stylis', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Stylis'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + '[namespace] {\n div {\n display: flex;\n\n @media screen {\n color: blue;\n }\n }\n\n div {\n transform: translateZ(0);\n\n h1, h2 {\n color: red;\n }\n }\n}', + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain( + '[namespace] div{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;}@media screen{[namespace] div{color:blue;}}[namespace] div{-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);}[namespace] div h1,[namespace] div h2{color:red;}', + ); + }); + + test('PostCSS/postcssImportUrl', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Import Url'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`@import "github-markdown-css";`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('.markdown-body'); + }); + + test('PostCSS/Autoprefixer', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Autoprefixer'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`.example { user-select: none; }`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('-webkit-user-select: none;'); + }); + + test('PostCSS/Preset Env', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Preset Env'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `:root { --mainColor: #12345678; --secondaryColor: lab(32.5 38.5 -47.6 / 90%); }`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('style'); + + expect(resultText).toContain('--mainColor: rgba(18,52,86,0.47059);'); + }); + + test('Babel', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Babel'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`[1, 2, 3].map(n => n + 1);`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `[1, 2, 3].map(function (n) { + return n + 1; +});`, + ); + }); + + test('Sucrase', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Sucrase'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`const Greet = (name: string) => <>Hello {name}!;`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `const Greet = (name) => React.createElement(React.Fragment, null, "Hello " , name, "!");`, + ); + }); + + test('TypeScript', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=TypeScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `type Fish = { swim: () => void }; +type Bird = { fly: () => void }; +declare function getSmallPet(): Fish | Bird; +// ---cut--- +function isFish(pet: Fish | Bird): pet is Fish { + return (pet as Fish).swim !== undefined; +}`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + `// ---cut--- +function isFish(pet) { + return pet.swim !== undefined; +}`, + ); + }); + + test('Flow', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Flow'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + 'function foo(x: ?number): string {if (x) { return x; } return "default string"; }', + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain( + 'function foo(x ) {if (x) { return x; } return "default string"; }', + ); + }); + + test('JSX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`
Loading...
`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="JSX"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +const Hello = (props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('TSX', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="TSX"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +interface Props { name: string } +const Hello = (props: Props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('React', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`
Loading...
`); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="React"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +const Hello = (props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('React (TSX)', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text="React (TSX)"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +interface Props { name: string } +const Hello = (props: Props) =>

Hello, {props.name}

+export default () => ; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, React`); + }); + + test('Vue', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Vue`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Vue JSX', async ({ page, getTestUrl }) => { + const sfc = ` + + +`; + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + await getResult().click('text=Click me'); + + const titleText = await getResult().innerText('h1'); + expect(titleText).toBe('Hello, Vue!'); + + const counterText = await getResult().innerText('text=You clicked'); + expect(counterText).toBe('You clicked 3 times.'); + }); + + test('Vue import', async ({ page, getTestUrl }) => { + const sfc = ` + + +`; + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + // css import + const headHTML = await getResult().innerHTML('head'); + expect(headHTML).toContain( + '', + ); + + // bare module import + const uuidText = await getResult().innerText('#uuid'); + expect(uuidText.length).toBeGreaterThan(10); + + // import vue component + await getResult().click(':nth-match(button, 1)'); + await getResult().click(':nth-match(button, 1)'); + await getResult().click(':nth-match(button, 1)'); + + // import vue component that has relative imports and fetches absolute url + const buttonText = await getResult().innerText(':nth-match(button, 1)'); + expect(buttonText).toBe('Count is: 3, double is: 6'); + }); + + test('Vue langs', async ({ page, getTestUrl }) => { + const sfc = ` + + + + +`; + + await page.goto( + getTestUrl({ + x: 'code/N4IgLglmA2CmIC4QFUB2kawCYAIAKATgPYBWsAxmCADQhawDO5BEADpEaoiDeAIYBzBogDaAXVp9KEAG6wAolihEC3Ji3a8AtnwIBrAK6tEoaH1QCDg+EgAWYLdF7lOYWOm4gAvrQZgAnnAmIGYWVgI2IOQMwrQu6O5USN6+zGxJpuaW1twyBvBxroncADxuWqxmbjihAgC8ADogWlhNAHwNBA2oODgAxDgAErDQ0ETUOMDAOKh8WrA4Xl4AhJ3dvSWsOAjkZjGNIAAkfoGwAHRuAB5g7QxE8zhXYCUA9KwdXaiv5ZV8bh-dNZfdTpGpZA5gBhNHAMWBgIwAno4eJ+GZzWAIGFgFgWHB1HAAcgAavkcABmAkAbiBrxB7ERNJOcDBFgOTBi0K0RCwBjgiN6hwAZq4ALR+KR6THDaBySDkPgTBjmBhi2AsQXUz4C1gsHQEfwilxjAiYvoAVgtmvWOGOFE4WF0BqNKkxAgIsHcVtQQN6tgAjJMfb0cML0JiAEwABkjAFIbaGwGKwBKvcHkURjZjDjqIHqnRmVKnFkCgxdYNdA1rgwnMX7o3GhaLxeQ9EXes6TTbYfEHfrDQWCEWvDSXkzYG0UiAxwxbB7IaIJFO0uxhAhxHEYoRGHDPLQdURyIw7gRV+uogY-PcAMpwyAWVfAHwgXOsFTzhCP2gBViMExPtx+A+IRZOEkTfowy5UIUCQeMkT5yCeECcNwAAs3hAA', + }), + ); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + // await page.keyboard.insertText(sfc); + + await waitForResultUpdate(); + + const headerText = await getResult().innerText('h1'); + + // markdown, scss, typescript + expect(headerText).toContain(`Hello, Vue 3!`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(85, 85, 85)'); + + // css modules + expect(await getResult().$eval('p', (e) => getComputedStyle(e).color)).toBe('rgb(0, 128, 0)'); + }); + + test('Vue + Tailwind CSS', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="vue"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` +`, + ); + + await app.click(':nth-match([title="Change Language"], 2)'); + await app.click('text=Tailwind CSS'); + await app.click('text=CSS'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `@tailwind base; +@tailwind components; +@tailwind utilities; +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Tailwind in Vue SFC`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe( + 'rgb(220, 38, 38)', + ); + }); + + test('Vue 2', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Vue 2'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Vue 2`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Svelte', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await (await app.$('[data-lang="svelte"]'))?.click(); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +
+

Hello, {title}

+
+`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Svelte`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Malina.js', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Malina.js'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + ` + +
+

Hello, {title}

+
+`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Malina.js`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('Stencil', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(''); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Stencil'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `import { Component, Prop, h } from "@stencil/core"; +@Component({ + tag: "my-app", + styles: "h1 { color: blue; }", +}) +export class App { + @Prop() title: string; + render() { + return ( +

Hello, {this.title}

+ ); + } +}`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('h1'); + + expect(resultText).toContain(`Hello, Stencil`); + expect(await getResult().$eval('h1', (e) => getComputedStyle(e).color)).toBe('rgb(0, 0, 255)'); + }); + + test('CoffeeScript', async ({ page, getTestUrl }) => { + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=CoffeeScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`square = (x) -> x * x`); + + await waitForResultUpdate(); + const resultText = await getResult().innerHTML('body > script'); + + expect(resultText).toContain(`var square;`); + expect(resultText).toContain(`square = function(x) { + return x * x; +};`); + }); + + test('LiveScript', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=LiveScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`{ capitalize, join, map, words } = require 'prelude-ls' +title = 'live script' +|> words +|> map capitalize +|> join '' +(document.getElementById \\title).textContent = title`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, LiveScript`); + }); + + test('Riot.js', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText(''); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Riot.js'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, {props.title}

'); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Riot.js`); + }); + + test('AssemblyScript', async ({ page, getTestUrl }) => { + test.fixme(); + + await page.goto(getTestUrl()); + + const { app, getResult } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText( + `

Hello, World

+ `, + ); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=AssemblyScript'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`export function getTitle(): string {return "AssemblyScript";`); + // workaround for monaco auto-complete + await page.keyboard.press('Delete'); + await page.keyboard.insertText(`}`); + + await app.waitForTimeout(15000); + const resultText = await getResult().innerText('text=Hello, AssemblyScript'); + + expect(resultText).toContain(`Hello, AssemblyScript`); + }); + + test('Python', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Python'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`from browser import document +title = 'Python' +document['header'].html = f"Hello, {title}"`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Python`); + }); + + test('Pyodide', async ({ page, getTestUrl }) => { + test.skip(); + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, World

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Pyodide'); + await waitForEditorFocus(app); + await page.keyboard.insertText(`from js import document +title = 'Python' +document.getElementById('header').innerHTML = f"Hello, {title}"`); + + await waitForResultUpdate(); + await getResult().waitForSelector('text=Hello, Python'); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Python`); + }); + + test('Ruby', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Ruby'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`require "native" +title = 'Ruby' +$$.document.querySelector('#title').innerHTML = title`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('text=Hello, Ruby'); + + expect(resultText).toContain(`Hello, Ruby`); + }); + + test('Go', async ({ page, getTestUrl, editor }) => { + test.slow(); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Go'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`package main +import "syscall/js" +func main() { + js.Global().Get("document").Call("querySelector", "#title").Set("innerHTML", "Golang") +}`); + + await waitForResultUpdate({ delay: 4000, timeout: 60_000 }); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Golang`); + }); + + test('PHP', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=PHP'); + await waitForEditorFocus(app); + + // go below pre-inserted 'getElementById('title')->textContent = $title;`, + ); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, PHP`); + }); + + test('Perl', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Perl'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`use strict; +my $title = 'Perl'; +JS::inline('document.getElementById("title").innerHTML') = $title;`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Perl`); + }); + + test('Lua', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Lua'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`js = require "js" +window = js.global +document = window.document +document:getElementById("title").innerHTML = "Lua"`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Lua`); + }); + + test('Scheme', async ({ page, getTestUrl, editor }) => { + test.skip(editor === 'codejar', 'please fix'); + + await page.goto(getTestUrl()); + + const { app, getResult, waitForResultUpdate } = await getLoadedApp(page); + + await app.click('text="HTML"'); + await waitForEditorFocus(app); + await page.keyboard.insertText('

Hello, world

'); + + await app.click(':nth-match([title="Change Language"], 3)'); + await app.click('text=Scheme'); + await waitForEditorFocus(app); + + await page.keyboard.insertText(`(let ((title "Scheme")) + (set-content! "#title" title))`); + + await waitForResultUpdate(); + const resultText = await getResult().innerText('h1'); + + expect(resultText).toContain(`Hello, Scheme`); + }); +}); diff --git a/functions/vendors/templates.js b/functions/vendors/templates.js index 10dbce55f7..2bbc7ed68f 100644 --- a/functions/vendors/templates.js +++ b/functions/vendors/templates.js @@ -447,10 +447,10 @@ body { font-size: 3.5rem; } } -`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:` +`.trimStart()},script:{language:"javascript",content:""},stylesheets:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/css/bootstrap.min.css"],scripts:["{{ __CDN_URL__ }}bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"],cssPreset:"",imports:{},types:{}};var y={name:"civet",title:getTemplateName("templates.starter.civet","Civet Starter"),thumbnail:"assets/templates/civet.png",activeEditor:"script",markup:{language:"html",content:`

Hello, World!

- +

You clicked 0 times.

@@ -463,25 +463,25 @@ body { .logo { width: 150px; } -`.trimStart()},script:{language:"coffeescript",content:` -titleElement = document.getElementById 'title' -counterElement = document.getElementById 'counter' -button = document.getElementById 'counter-button' +`.trimStart()},script:{language:"civet",content:` +titleElement := document.getElementById 'title' +counterElement := document.getElementById 'counter' +button := document.getElementById 'counter-button' -title = 'CoffeeScript' +title := 'Civet' titleElement.innerText = title -counter = (count) -> -> count += 1 -increment = counter 0 +counter := (count: number) => => count += 1 +increment := counter 0 +function handleClick: void counterElement.innerText = increment() -button.addEventListener('click', - -> counterElement.innerText = increment()) -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"go",title:getTemplateName("templates.starter.go","Go Starter"),thumbnail:"assets/templates/go.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener 'click', handleClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var x={name:"clio",title:getTemplateName("templates.starter.clio","Clio Starter"),thumbnail:"assets/templates/clio.png",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

- +
`.trimStart()},style:{language:"css",content:` .container, @@ -490,62 +490,38 @@ button.addEventListener('click', font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"go",content:` -package main - -import ( - "fmt" - "syscall/js" - "time" -) - -func main() { - title := querySelector("#title") - title.Set("innerHTML", "Golang") +`.trimStart()},script:{language:"clio",content:` +fn capitalize str: + (str.charAt 0 -> .toUpperCase) + (str.slice 1 -> .toLowerCase) - registerCounter() +fn greet name: + f"Hello, {name}!" - // yes, you can use goroutines (check the console) - go greet() - fmt.Println("Hello!") -} +fn setTitle name: + title = document.querySelector "#title" + title.innerText = name -> capitalize -> greet -func querySelector(id string) js.Value { - return js.Global().Get("document").Call("querySelector", id) -} +fn increment value: + (Number value) + 1 -func registerCounter() { - btn := querySelector("#counter-button") - counter := querySelector("#counter") - count := 0 +fn activateBtn btn: + btn.disabled = false + btn.innerText = "Click me" + btn - var cb js.Func - cb = js.FuncOf(func(this js.Value, args []js.Value) interface{} { - count += 1 - counter.Set("innerHTML", count) - return nil - }) - btn.Call("addEventListener", "click", cb) -} +fn onBtnClick: + counter = document.querySelector "#counter" + counter.innerText = increment counter.innerText -func greet() { - if hours, _, _ := time.Now().Clock(); hours < 12 { - fmt.Println("Good morning") - } else if hours < 18 { - fmt.Println("Good afternoon") - } else { - fmt.Println("Good evening") - } -} -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"jquery",title:getTemplateName("templates.starter.jquery","jQuery Starter"),thumbnail:"assets/templates/jquery.svg",activeEditor:"script",markup:{language:"html",content:` -
-

Hello, World!

- -

You clicked 0 times.

- -
+export fn main argv: + setTitle "clio" + document.querySelector "#counter-button" + -> activateBtn + -> .addEventListener "click" onBtnClick +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var w={name:"clojurescript",title:getTemplateName("templates.starter.clojurescript","ClojureScript Starter"),thumbnail:"assets/templates/cljs.svg",activeEditor:"script",markup:{language:"html",content:` +
Loading...
`.trimStart()},style:{language:"css",content:` .container, .container button { @@ -553,24 +529,43 @@ func greet() { font: 1em sans-serif; } .logo { - width: 300px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import $ from "jquery"; +`.trimStart()},script:{language:"clojurescript",content:` +(ns react.component + (:require + ;; you may use npm packages + ["canvas-confetti$default" :as confetti] + ["react$default" :as React] + ["react" :refer [useState]] + ["react-dom/client" :refer [createRoot]])) -$("#title").text('jQuery'); +(defn Counter [^:js {:keys [name]}] + (let [[counter setCount] (useState 0)] + #jsx [:div + {:className "container"} + [:h1 (str "Hello, " name "!")] + [:img + {:className "logo" + :alt "logo" + :src "{{ __livecodes_baseUrl__ }}assets/templates/cljs.svg"}] + [:p "You clicked " counter " times."] + [:button + {:onClick (fn [] + (if (= (mod counter 3) 0) (confetti)) + (setCount (inc counter)))} + "Click me"]])) -let count = 0; -$("#counter-button").click(() => { - count += 1; - $("#counter").text(count); -}); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var S={name:"knockout",title:getTemplateName("templates.starter.knockout","Knockout Starter"),thumbnail:"assets/templates/knockout.svg",activeEditor:"script",markup:{language:"html",content:` +(def title "ClojureScript") +(print (str "Hello, " title "!")) +(defonce root (createRoot (js/document.querySelector "#app"))) +(.render root #jsx [Counter #js {:name title}]) +`.trimStart()}};var S={name:"coffeescript",title:getTemplateName("templates.starter.coffeescript","CoffeeScript Starter"),thumbnail:"assets/templates/coffeescript.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

- +

Hello, World!

+ +

You clicked 0 times.

+
`.trimStart()},style:{language:"css",content:` .container, @@ -579,27 +574,25 @@ $("#counter-button").click(() => { font: 1em sans-serif; } .logo { - width: 250px; + width: 150px; } -`.trimStart()},script:{language:"javascript",content:` -import ko from "knockout"; +`.trimStart()},script:{language:"coffeescript",content:` +titleElement = document.getElementById 'title' +counterElement = document.getElementById 'counter' +button = document.getElementById 'counter-button' -class ClickCounterViewModel { - constructor() { - this.title = 'Knockout'; - this.numberOfClicks = ko.observable(0); +title = 'CoffeeScript' +titleElement.innerText = title - this.registerClick = function () { - this.numberOfClicks(this.numberOfClicks() + 1); - }; - } -} +counter = (count) -> -> count += 1 +increment = counter 0 -ko.applyBindings(new ClickCounterViewModel()); -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"livescript",title:getTemplateName("templates.starter.livescript","LiveScript Starter"),thumbnail:"assets/templates/livescript.svg",activeEditor:"script",markup:{language:"html",content:` +button.addEventListener('click', + -> counterElement.innerText = increment()) +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var k={name:"commonlisp",title:getTemplateName("templates.starter.commonlisp","Common Lisp Starter"),thumbnail:"assets/templates/commonlisp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- +

Hello, World!

+

You clicked 0 times.

@@ -612,31 +605,70 @@ ko.applyBindings(new ClickCounterViewModel()); .logo { width: 150px; } -`.trimStart()},script:{language:"livescript",content:` -{ capitalize, join, map, words } = require 'prelude-ls' - -title = 'live script' -|> words -|> map capitalize -|> join '' - -(document.getElementById \\title).innerText = title +`.trimStart()},script:{language:"commonlisp",content:` +(defun set-attribute (&key selector attribute value) + (let ((node + (#j:document:querySelector selector))) + (setf (jscl::oget node attribute) value) + node)) -increment = (count) -> -> count += 1 -counter = increment 0 +(let ((title "Common Lisp")) + (set-attribute :selector "#title" :attribute "innerHTML" + :value (format nil "Hello, ~A!" title))) -counter-element = document.getElementById \\counter -button = document.getElementById \\counter-button +(let ((counter 0)) + (set-attribute :selector "#counter-button" :attribute "onclick" + :value #'(lambda (ev) + (setf counter (+ counter 1)) + (set-attribute :selector "#counter" :attribute "innerHTML" + :value counter)))) -button.addEventListener \\click, - -> counter-element.innerText = counter! -`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"lua",title:getTemplateName("templates.starter.lua","Lua Starter"),thumbnail:"assets/templates/lua.svg",activeEditor:"script",markup:{language:"html",content:` +(#j:console:clear) +(write "Hello, Common Lisp!") +`.trimStart()},stylesheets:[],scripts:[],cssPreset:"",imports:{},types:{}};var _={name:"cpp",title:getTemplateName("templates.starter.cpp","C++ Starter"),thumbnail:"assets/templates/cpp.svg",activeEditor:"script",markup:{language:"html",content:`
-

Hello, World!

- -

You clicked 0 times.

+

Hello, World!

+ +

You clicked 0 times.

+ + +`.trimStart(), + }, + style: { + language: 'css', + content: ` +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +.logo { + width: 150px; + display: block; + margin: 20px auto; +} + +.demo-section { + background: #f5f5f5; + padding: 20px; + margin: 20px 0; + border-radius: 8px; + border-left: 4px solid #00add8; +} + +.demo-section h2 { + margin-top: 0; + color: #333; +} + +button { + background: #00add8; + color: white; + border: none; + padding: 10px 20px; + border-radius: 4px; + cursor: pointer; + font-size: 16px; + margin: 10px 5px; +} + +button:hover:not(:disabled) { + background: #0099c7; +} + +button:disabled { + background: #ccc; + cursor: not-allowed; +} + +input[type="text"], input[type="number"] { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 16px; + margin: 5px; + width: 200px; +} + +#counter { + font-weight: bold; + color: #00add8; + font-size: 24px; +} + +#greeting, #result { + font-weight: bold; + color: #333; + margin-top: 10px; +} +`.trimStart(), + }, + script: { + language: 'go-wasm', + content: ` +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func main() { + // Read input from stdin + scanner := bufio.NewScanner(os.Stdin) + + if scanner.Scan() { + input := strings.TrimSpace(scanner.Text()) + + if count, err := strconv.Atoi(input); err == nil { + newCount := count + 1 + fmt.Println(newCount) + return + } + + fmt.Printf("Hello, %s!\\n", input) + } else { + fmt.Println("Hello from Go WebAssembly!") + } +} +`.trimStart(), + }, +}; diff --git a/src/livecodes/templates/starter/index.ts b/src/livecodes/templates/starter/index.ts index 1075d26834..9f8a22eb10 100644 --- a/src/livecodes/templates/starter/index.ts +++ b/src/livecodes/templates/starter/index.ts @@ -21,6 +21,7 @@ import { diagramsStarter } from './diagrams-starter'; import { fennelStarter } from './fennel-starter'; import { gleamStarter } from './gleam-starter'; import { goStarter } from './go-starter'; +import { goWasmStarter } from './go-wasm-starter'; import { imbaStarter } from './imba-starter'; import { javaStarter } from './java-starter'; import { javascriptStarter } from './javascript-starter'; @@ -111,6 +112,7 @@ export const starterTemplates = [ rubyStarter, rubyWasmStarter, goStarter, + goWasmStarter, phpStarter, phpWasmStarter, cppStarter, diff --git a/src/livecodes/vendors.ts b/src/livecodes/vendors.ts index 3ee6afdcfc..2207984b32 100644 --- a/src/livecodes/vendors.ts +++ b/src/livecodes/vendors.ts @@ -99,6 +99,8 @@ export const cppWasmBaseUrl = /* @__PURE__ */ getUrl('@chriskoch/cpp-wasm@1.0.2/ export const csharpWasmBaseUrl = /* @__PURE__ */ getUrl('@seth0x41/csharp-wasm@1.0.3/'); +export const yaegiWasmBaseUrl = /* @__PURE__ */ getUrl('yaegi-wasm@1.0.2/src/'); + export const csstreeUrl = /* @__PURE__ */ getUrl('css-tree@2.3.1/dist/csstree.js'); export const cytoscapeSvgUrl = /* @__PURE__ */ getUrl('cytoscape-svg@0.4.0/cytoscape-svg.js'); diff --git a/src/sdk/models.ts b/src/sdk/models.ts index 50695a9bdd..adebd1a1ae 100644 --- a/src/sdk/models.ts +++ b/src/sdk/models.ts @@ -968,6 +968,8 @@ export type Language = | 'riotjs' | 'malina' | 'malinajs' + | 'ripple' + | 'ripplejs' | 'xht' | 'coffeescript' | 'coffee' @@ -998,6 +1000,9 @@ export type Language = | 'rubywasm' | 'go' | 'golang' + | 'go-wasm' + | 'wasm.go' + | 'gowasm' | 'php' | 'php-wasm' | 'phpwasm' @@ -1350,6 +1355,7 @@ export interface Compiler { | 'text/commonlisp' | 'text/tcl' | 'text/prolog' + | 'text/go-wasm' | 'application/json' | 'application/lua' | 'text/fennel' @@ -1418,6 +1424,7 @@ export type TemplateName = | 'ruby' | 'ruby-wasm' | 'go' + | 'go-wasm' | 'php' | 'php-wasm' | 'cpp' diff --git a/vendor-licenses.md b/vendor-licenses.md index c3049f5b7c..a22b29a64e 100644 --- a/vendor-licenses.md +++ b/vendor-licenses.md @@ -315,3 +315,7 @@ WebR: [License](https://github.com/r-wasm/webr/blob/e47ab9854c9306c410f302579c66 Whirl: [MIT License](https://github.com/jh3y/whirl/tree/8de79b76a13200ccbd8b0b75b4f79978ef1ee890) Windi CSS: [MIT License](https://github.com/windicss/windicss/blob/bcd50d877e62630f191602ddeabd9f677cc6d90c/LICENSE) + +Yaegi: [Apache License 2.0](https://github.com/traefik/yaegi/blob/d93266d013f393a20122ef0dd3f579d411a066be/LICENSE) + +yaegi-wasm: [Apache License 2.0](https://github.com/Muhammad-Ayman/yaegi-wasm/blob/528a6bbac7464dc8911e50e0d617512f3e247e8c/LICENSE)