import { Router, DomAppender, DomWriter } from '../src/index' import { mockDom, wait } from './utils' import * as assert from 'assert' describe('DomWriter', () => { beforeEach(mockDom) describe('outside of request context', () => { it('should throw an exception', () => { const router = new Router() const renderer = new DomWriter(router) const div = document.createElement('div') assert.throws( () => renderer.render(div) ) }) }) describe('renderContent', () => { describe('when req.id is empty', () => { it('id of created element should be empty', async () => { document.body.innerHTML = '' const router = new Router() const writer = new DomWriter(router) router.route(() => router.defer(() => writer.renderContent('test'))) router.listen() window.location.hash = '#' await wait() assert.strictEqual(document.body.children.length, 1) assert.strictEqual(document.body.firstElementChild!.id, '') assert.strictEqual(document.body.firstElementChild!.textContent, 'test') }) }) describe('when req.id is non-empty', () => { it('id of created element should not be empty', async () => { document.body.innerHTML = '' const router = new Router() const writer = new DomWriter(router) router.route(() => router.defer(() => writer.renderContent('test'))) router.listen() window.location.hash = '#foo' await wait() assert.strictEqual(document.body.children.length, 1) assert.strictEqual(document.body.firstElementChild!.id, 'foo') assert.strictEqual(document.body.firstElementChild!.textContent, 'test') }) }) describe('when there is an existing fragment with the same ID as request', () => { describe('outside the container', () => { it('should not mess with the existing fragment', async () => { document.body.innerHTML = `
No!
` const router = new Router() const writer = new DomWriter(router, { container: document.querySelector('.container') }) router.route(() => router.defer(() => writer.renderContent('test'))) router.listen() window.location.hash = '#foo' await wait() assert.strictEqual(document.getElementById('foo')!.textContent, 'No!') const foo = document.querySelector('.container div') assert.ok(foo!) assert.strictEqual(foo.tagName, 'DIV') assert.strictEqual(foo.id, 'foo') assert.strictEqual(foo.textContent, 'test') }) }) }) }) describe('restore', () => { describe('when request ID is the same as the ID of an existing fragment', () => { it('should save and restore existing fragment after switching to another hash', async () => { const div = document.querySelector('#foo') assert.ok(div) div.textContent = 'yay' const router = new Router() const writer = new DomWriter(router) router.route(req => { router.defer(() => { writer.renderContent(`Hello, ${req.id}!`) }) }).listen() window.location.hash = '#foo' await wait() const foo = document.querySelector('#foo') assert.ok(Boolean(foo)) assert.strictEqual(foo?.textContent, 'Hello, foo!') window.location.hash = '#bar' await wait() assert.ok(document.querySelector('#foo')) assert.strictEqual( document.querySelector('#foo')!.textContent, 'yay' ) const bar = document.querySelector('#bar') assert.ok(Boolean(bar)) assert.strictEqual(bar?.textContent, 'Hello, bar!') }) }) }) describe('with no specified container in options', () => { it('should insert elements into document.body', async () => { document.body.innerHTML = '' const router = new Router() const writer = new DomWriter(router) router.route(req => { router.defer(() => { writer.renderContent(`Hello, ${req.id}!`) }) }).listen() window.location.hash = '#foo' await wait() const foo = document.querySelector('#foo') assert.ok(Boolean(foo)) assert.strictEqual(foo?.textContent, 'Hello, foo!') window.location.hash = '#bar' await wait() assert.ok(!document.querySelector('#foo')) const bar = document.querySelector('#bar') assert.ok(Boolean(bar)) assert.strictEqual(bar?.textContent, 'Hello, bar!') }) }) describe('with non-HTMLElement', () => { it('should insert div with result as textContent', async () => { const router = new Router() const writer = new DomWriter(router) router.route(() => { router.defer(() => { writer.renderContent([1, 2, 3]) }) }).listen() window.location.hash = '#test' await wait() const div = document.querySelector('#test')! assert.ok(Boolean(div)) assert.strictEqual(div.tagName, 'DIV') assert.strictEqual(div.textContent, [1, 2, 3].toString()) }) }) describe('with container in options', () => { it('should insert elements into container', async () => { document.body.innerHTML = '
' const container = document.querySelector('#test') const router = new Router() const writer = new DomWriter(router, { container }) router.route(req => { router.defer(() => { writer.renderContent(`Hello, ${req.id}!`) }) }).listen() window.location.hash = '#foo' await wait() const foo = document.querySelector('#foo') assert.ok(Boolean(foo)) assert.strictEqual(foo?.textContent, 'Hello, foo!') assert.deepStrictEqual(container, foo.parentNode) window.location.hash = '#bar' await wait() assert.ok(!document.querySelector('#foo')) const bar = document.querySelector('#bar') assert.ok(Boolean(bar)) assert.strictEqual(bar?.textContent, 'Hello, bar!') assert.deepStrictEqual(container, bar.parentNode) }) }) }) describe('DomAppender', () => { beforeEach(mockDom) describe('with no specified container in options', () => { it('should append elements into document.body', async () => { document.body.innerHTML = '' const router = new Router() const appender = new DomAppender(router) router.route(req => { router.defer(() => { appender.renderContent(`Hello, ${req.id}!`) }) }).listen() window.location.hash = '#foo' await wait() const foo = document.querySelector('#foo') assert.ok(Boolean(foo)) assert.strictEqual(foo?.textContent, 'Hello, foo!') window.location.hash = '#bar' await wait() assert.ok(document.querySelector('#foo')) const bar = document.querySelector('#bar') assert.ok(Boolean(bar)) assert.strictEqual(bar?.textContent, 'Hello, bar!') }) }) describe('with container in options', () => { it('should append elements into container', async () => { document.body.innerHTML = '
' const container = document.querySelector('#test') const router = new Router() const appender = new DomAppender(router, { container }) router.route(req => { router.defer(() => { appender.renderContent(`Hello, ${req.id}!`) }) }).listen() window.location.hash = '#foo' await wait() const foo = document.querySelector('#foo') assert.ok(Boolean(foo)) assert.strictEqual(foo?.textContent, 'Hello, foo!') assert.deepStrictEqual(container, foo.parentNode) window.location.hash = '#bar' await wait() assert.ok(document.querySelector('#foo')) const bar = document.querySelector('#bar') assert.ok(Boolean(bar)) assert.strictEqual(bar?.textContent, 'Hello, bar!') assert.deepStrictEqual(container, bar.parentNode) }) }) describe('with non-HTMLElement', () => { it('should append div with result as textContent', async () => { const router = new Router() const appender = new DomAppender(router) router.route(() => { router.defer(() => { appender.renderContent([1, 2, 3]) }) }).listen() window.location.hash = '#test' await wait() const div = document.querySelector('#test')! assert.ok(Boolean(div)) assert.strictEqual(div.tagName, 'DIV') assert.strictEqual(div.textContent, [1, 2, 3].toString()) }) }) describe('outside of request context', () => { it('should throw an exception', () => { const router = new Router() const renderer = new DomAppender(router) const div = document.createElement('div') assert.throws( () => renderer.render(div) ) }) }) describe('renderHtml', () => { it('should convert html to element and append it to the document', async () => { document.body.innerHTML = '' const router = new Router() const appender = new DomAppender(router) router.route( req => { router.defer(() => { appender.renderHtml(`

${req.id}

`) }) } ).listen() window.location.hash = '#test' await wait() const h1 = document.querySelector('#test')! assert.ok(Boolean(h1)) assert.strictEqual(h1.tagName, 'H1') assert.strictEqual(h1.id, 'test') assert.strictEqual(h1.textContent, 'test') }) }) })