blob: 02a468b0394d090d35cf28a5bc35f492497acd4b [file] [log] [blame]
/*!
* @license
* Copyright 2020 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
import { PlaygroundExampleController } from './playground';
import { mocked } from 'ts-jest/utils';
const flushPromises = () => new Promise(fn => setTimeout(fn, 0));
const el = <T extends HTMLElement>(selector: string) => document.querySelector<T>(selector);
const codeSnippet = `package main
import (
"fmt"
"io"
"os"
)
func main() {
const name, age = "Kim", 22
s := fmt.Sprintln( name, "is", age, "years old.")
io.WriteString( os.Stdout, s) // Ignoring error for simplicity.
// HTML special characters: & ' < > "
}
`;
const escapeHTML = (s: string) => {
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/'/g, '&#39;')
.replace(/"/g, '&#34;');
};
describe('PlaygroundExampleController', () => {
let example: HTMLDetailsElement;
window.fetch = jest.fn();
window.open = jest.fn();
beforeEach(() => {
document.body.innerHTML = `
<details tabindex="-1" id="example-Sprintln" class="Documentation-exampleDetails js-exampleContainer">
<summary class="Documentation-exampleDetailsHeader">Example <a href="#example-Sprintln">ΒΆ</a></summary>
<div class="Documentation-exampleDetailsBody">
<p>Code:</p>
<pre class="Documentation-exampleCode">${escapeHTML(codeSnippet)}</pre>
<pre>
<span class="Documentation-exampleOutputLabel">Output:</span>
<span class="Documentation-exampleOutput">Kim is 22 years old.</span>
</pre>
</div>
<div class="Documentation-exampleButtonsContainer">
<p class="Documentation-exampleError" role="alert" aria-atomic="true"></p>
<button class="Documentation-exampleShareButton" aria-label="Share Code">Share</button>
<button class="Documentation-exampleFormatButton" aria-label="Format Code">Format</button>
<button class="Documentation-exampleRunButton" aria-label="Run Code">Run</button>
</div></details>
`;
example = el('.js-exampleContainer') as HTMLDetailsElement;
new PlaygroundExampleController(example);
});
afterEach(() => {
document.body.innerHTML = '';
mocked(window.fetch).mockClear();
mocked(window.open).mockClear();
});
it('expands and collapses example when summary is clicked', () => {
const summary = example.firstElementChild as HTMLDetailsElement;
summary.click();
expect(example.open).toBeTruthy();
summary.click();
expect(example.open).toBeFalsy();
});
it('replaces the pre element with a text area', () => {
const input = document.querySelector('.Documentation-exampleCode');
expect(input.tagName).toBe('TEXTAREA');
});
it('opens playground after pressing share', async () => {
mocked(window.fetch).mockResolvedValue({
text: () => Promise.resolve('abcdefg'),
} as Response);
el('[aria-label="Share Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/share', {
body: codeSnippet,
method: 'POST',
});
expect(window.open).toHaveBeenCalledWith('https://play.golang.org/p/abcdefg');
});
it('replaces textarea with formated code after pressing format', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Body: '// mocked response',
Error: '',
}),
} as Response);
el('[aria-label="Format Code"]').click();
const body = new FormData();
body.append('body', codeSnippet);
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/fmt', {
body: body,
method: 'POST',
});
expect(el<HTMLTextAreaElement>('.Documentation-exampleCode').value).toBe('// mocked response');
});
it('displays error message after pressing format with invalid code', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Body: '',
Error: '// mocked error',
}),
} as Response);
el('[aria-label="Format Code"]').click();
const body = new FormData();
body.append('body', codeSnippet);
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/fmt', {
body: body,
method: 'POST',
});
expect(el<HTMLTextAreaElement>('.Documentation-exampleCode').value).toBe(codeSnippet);
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked error');
});
it('displays code output after pressing run', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Events: [{ Message: '// mocked response', Kind: 'stdout', Delay: 0 }],
Errors: null,
}),
} as Response);
el('[aria-label="Run Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/compile', {
body: JSON.stringify({ body: codeSnippet, version: 2 }),
method: 'POST',
});
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked response');
});
it('displays error message after pressing run with invalid code', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Events: null,
Errors: '// mocked error',
}),
} as Response);
el('[aria-label="Run Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/compile', {
body: JSON.stringify({ body: codeSnippet, version: 2 }),
method: 'POST',
});
expect(el('.Documentation-exampleOutput').textContent).toContain('// mocked error');
});
it('escapes example output', async () => {
mocked(window.fetch).mockResolvedValue({
json: () =>
Promise.resolve({
Events: [{ Message: '\u003cinput required\u003e', Kind: 'stdout', Delay: 0 }],
Errors: null,
}),
} as Response);
el('[aria-label="Run Code"]').click();
await flushPromises();
expect(window.fetch).toHaveBeenCalledWith('/play/compile', {
body: JSON.stringify({ body: codeSnippet, version: 2 }),
method: 'POST',
});
expect(el('.Documentation-exampleOutput').innerHTML).toBe('&lt;input required&gt;');
});
});