Skip to content

Commit

Permalink
Have StringCalculatorPage use host document
Browse files Browse the repository at this point in the history
StringCalculatorPage no longer creates its own DocumentFragment with a
new top level app element (<div id="app">).

The constructor now takes the top level app element as its first
argument, and an optional Document as its second argument. This enables
it to continue wrapping documents created via the PageLoader from
test/helpers.js, as used in main.test.js.

The new static factory, StringCalculatorPage.new(), creates a new <div>
with the specified ID, appends it document.body, and returns a new
StaticCalculatorPage wrapping that app element. This tries to ensure
that the <div>s created by tests don't collide with existing <div>s in
the document (short of randomly generating its own IDs).

StringCalculatorPage.new() also  keeps track of instances it creates,
and clients should call StringCalculatorPage.cleanup() in their
afterEach() handlers. This ensures the host document returns to its
original state after each test.

---

As mentioned in the previous couple of commits, a <form> element
attached to a DocumentFragment would exhibit inconsistent behavior
across jsdom, Chrome, and Firefox. This new implementation solves that
problem (as will be seen in the upcoming Calculator component tests).

It would've been nice to use a DocumentFragment for all our tests
instead of modifying the global document in any way. However, this way
we can be more confident that our components will exhibit consistent
behavior regardless of the test environment.
  • Loading branch information
mbland committed Dec 16, 2023
1 parent 2d9a631 commit 5f2a917
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 12 deletions.
8 changes: 5 additions & 3 deletions strcalc/src/main/frontend/components/init.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import initApp from './init'
import { describe, expect, test } from 'vitest'
import { afterEach, describe, expect, test } from 'vitest'
import StringCalculatorPage from '../test/page'

// @vitest-environment jsdom
describe('initial state after calling initApp', () => {
afterEach(() => StringCalculatorPage.cleanup())

test('contains the "Hello, World!" placeholder', async () => {
const page = new StringCalculatorPage()
const page = StringCalculatorPage.new('app-init')

initApp(window, page.document, page.appElem)
initApp(window, document, page.appElem, {})

const e = page.placeholder()
expect(e.textContent).toContain('Hello, World!')
Expand Down
1 change: 0 additions & 1 deletion strcalc/src/main/frontend/components/placeholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import Template from './placeholder.hbs'

export default class Placeholder {

/**
* Initializes the Placeholder within the document.
* @param {Window} window - the browser window object
Expand Down
3 changes: 2 additions & 1 deletion strcalc/src/main/frontend/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ describe('String Calculator UI on initial page load', () => {

test('contains the "Hello, World!" placeholder', async () => {
const { document } = await loader.load('index.html')
const appElem = document.querySelector('#app')

const e = new StringCalculatorPage(document).placeholder()
const e = new StringCalculatorPage(appElem, document).placeholder()
expect(e.textContent).toContain('Hello, World!')
expect(e.href).toContain('%22Hello,_World!%22')
})
Expand Down
24 changes: 17 additions & 7 deletions strcalc/src/main/frontend/test/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { fragment } from './helpers'

/**
* Represents the StringCalculator web page.
*
Expand All @@ -15,15 +13,27 @@ import { fragment } from './helpers'
* @see https://www.selenium.dev/documentation/test_practices/design_strategies/
*/
export default class StringCalculatorPage {
document
static #pages = []

appElem
#select

constructor(doc) {
this.document = doc !== undefined ? doc : fragment('<div id="app"></div>')
this.appElem = this.document.querySelector('#app')
this.#select = s => this.document.querySelector(`#${this.appElem.id} ${s}`)
constructor(appElem, doc = document) {
this.appElem = appElem
this.#select = sel => doc.querySelector(`#${appElem.id} ${sel}`)
}

static new(appElemId) {
const appElem = document.createElement('div')
appElem.id = appElemId
document.body.appendChild(appElem)

const page = new StringCalculatorPage(appElem)
this.#pages.push(page)
return page
}

static cleanup() { this.#pages.forEach(p => p.appElem.remove()) }

placeholder() { return this.#select('.placeholder a') }
}

0 comments on commit 5f2a917

Please sign in to comment.