Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement test helper logging #745

Merged
merged 2 commits into from
Dec 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/-logging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Target from './-target';

/**
* Logs a debug message to the console if the `testHelperLogging` query
* parameter is set.
*
* @private
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, I wonder if this should actually be private. Seems like it might be nice for folks authoring custom helpers too...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say let's try it out and see if it works properly before we commit to making it a public API

* @param {string} helperName Name of the helper
* @param {string|Element} target The target element or selector
*/
export function log(helperName: string, target: Target, ...args: any[]) {
if (loggingEnabled()) {
// eslint-disable-next-line no-console
console.log(`${helperName}(${[elementToString(target), ...args.filter(Boolean)].join(', ')})`);
}
}

/**
* Returns whether the test helper logging is enabled or not via the
* `testHelperLogging` query parameter.
*
* @private
* @returns {boolean} true if enabled
*/
function loggingEnabled(): boolean {
return typeof location !== 'undefined' && location.search.indexOf('testHelperLogging') !== -1;
}

/**
* This generates a human-readable description to a DOM element.
*
* @private
* @param {*} el The element that should be described
* @returns {string} A human-readable description
*/
export function elementToString(el: unknown) {
let desc: string;
if (el instanceof NodeList) {
if (el.length === 0) {
return 'empty NodeList';
}
desc = Array.prototype.slice
.call(el, 0, 5)
.map(elementToString)
.join(', ');
return el.length > 5 ? `${desc}... (+${el.length - 5} more)` : desc;
}
if (!(el instanceof HTMLElement || el instanceof SVGElement)) {
return String(el);
}

desc = el.tagName.toLowerCase();
if (el.id) {
desc += `#${el.id}`;
}
if (el.className && !(el.className instanceof SVGAnimatedString)) {
desc += `.${String(el.className).replace(/\s+/g, '.')}`;
}
Array.prototype.forEach.call(el.attributes, function(attr: Attr) {
if (attr.name !== 'class' && attr.name !== 'id') {
desc += `[${attr.name}${attr.value ? `="${attr.value}"]` : ']'}`;
}
});
return desc;
}
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/blur.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import settled from '../settled';
import isFocusable from './-is-focusable';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
@private
Expand Down Expand Up @@ -51,6 +52,8 @@ export function __blur__(element: HTMLElement | SVGElement): void {
blur('input');
*/
export default function blur(target: Target = document.activeElement!): Promise<void> {
log('blur', target);

return nextTickPromise().then(() => {
let element = getElement(target);
if (!element) {
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import isFocusable from './-is-focusable';
import { nextTickPromise } from '../-utils';
import isFormControl from './-is-form-control';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
@private
Expand Down Expand Up @@ -69,6 +70,8 @@ export function __click__(element: Element | Document, options: MouseEventInit):
click('button', { shiftKey: true });
*/
export default function click(target: Target, options: MouseEventInit = {}): Promise<void> {
log('click', target);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `click`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/double-click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import settled from '../settled';
import isFocusable from './-is-focusable';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
@private
Expand Down Expand Up @@ -80,6 +81,8 @@ export function __doubleClick__(element: Element | Document, options: MouseEvent
doubleClick('button', { shiftKey: true });
*/
export default function doubleClick(target: Target, options: MouseEventInit = {}): Promise<void> {
log('doubleClick', target);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `doubleClick`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/fill-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import settled from '../settled';
import fireEvent from './fire-event';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
Fill the provided text into the `value` property (or set `.innerHTML` when
Expand All @@ -24,6 +25,8 @@ import Target from './-target';
fillIn('input', 'hello world');
*/
export default function fillIn(target: Target, text: string): Promise<void> {
log('fillIn', target, text);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `fillIn`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/focus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import settled from '../settled';
import isFocusable from './-is-focusable';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
@private
Expand Down Expand Up @@ -54,6 +55,8 @@ export function __focus__(element: HTMLElement | SVGElement): void {
focus('input');
*/
export default function focus(target: Target): Promise<void> {
log('focus', target);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `focus`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/tap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { __click__ } from './click';
import settled from '../settled';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
Taps on the specified target.
Expand Down Expand Up @@ -48,6 +49,8 @@ import Target from './-target';
tap('button');
*/
export default function tap(target: Target, options: object = {}): Promise<void> {
log('tap', target);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `tap`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/trigger-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fireEvent from './fire-event';
import settled from '../settled';
import { nextTickPromise } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

/**
* Triggers an event on the specified target.
Expand Down Expand Up @@ -52,6 +53,8 @@ export default function triggerEvent(
eventType: string,
options?: object
): Promise<void> {
log('triggerEvent', target, eventType);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `triggerEvent`.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import settled from '../settled';
import { KEYBOARD_EVENT_TYPES, KeyboardEventType, isKeyboardEventType } from './fire-event';
import { nextTickPromise, isNumeric } from '../-utils';
import Target from './-target';
import { log } from '@ember/test-helpers/dom/-logging';

export interface KeyModifiers {
ctrlKey?: boolean;
Expand Down Expand Up @@ -183,6 +184,8 @@ export default function triggerKeyEvent(
key: number | string,
modifiers: KeyModifiers = DEFAULT_MODIFIERS
): Promise<void> {
log('triggerKeyEvent', target, eventType, key);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `triggerKeyEvent`.');
Expand Down
3 changes: 3 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/type-in.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Promise } from 'rsvp';
import fireEvent from './fire-event';
import Target from './-target';
import { __triggerKeyEvent__ } from './trigger-key-event';
import { log } from '@ember/test-helpers/dom/-logging';

export interface Options {
delay?: number;
Expand Down Expand Up @@ -38,6 +39,8 @@ export interface Options {
* typeIn('hello world');
*/
export default function typeIn(target: Target, text: string, options: Options = {}): Promise<void> {
log('typeIn', target, text);

return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `typeIn`.');
Expand Down
97 changes: 97 additions & 0 deletions tests/unit/dom/logging-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { module, test } from 'qunit';
import {
render,
settled,
setupContext,
setupRenderingContext,
teardownContext,
teardownRenderingContext,
} from '@ember/test-helpers';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { elementToString } from '@ember/test-helpers/dom/-logging';
import hbs from 'htmlbars-inline-precompile';

module('elementToString()', function(hooks) {
if (!hasEmberVersion(2, 4)) {
return;
}

hooks.beforeEach(async function() {
await setupContext(this);
await setupRenderingContext(this);
});

hooks.afterEach(async function() {
await settled();
await teardownRenderingContext(this);
await teardownContext(this);
});

module('NodeLists', function() {
test('empty NodeList', async function(assert) {
await render(hbs``);
assert.equal(elementToString(this.element.querySelectorAll('h1')), 'empty NodeList');
});

test('with single element', async function(assert) {
await render(hbs`<h1></h1>`);
assert.equal(elementToString(this.element.querySelectorAll('h1')), 'h1');
});

test('with three elements', async function(assert) {
await render(hbs`<h1></h1><h1></h1><h1 class="foo"></h1>`);
assert.equal(elementToString(this.element.querySelectorAll('h1')), 'h1, h1, h1.foo');
});

test('with five elements', async function(assert) {
await render(hbs`<h1></h1><h1></h1><h1 class="foo"></h1><h1></h1><h1></h1>`);
assert.equal(elementToString(this.element.querySelectorAll('h1')), 'h1, h1, h1.foo, h1, h1');
});

test('with six elements', async function(assert) {
await render(hbs`<h1></h1><h1></h1><h1 class="foo"></h1><h1></h1><h1></h1><h1></h1>`);
assert.equal(
elementToString(this.element.querySelectorAll('h1')),
'h1, h1, h1.foo, h1, h1... (+1 more)'
);
});

test('with ten elements', async function(assert) {
await render(
hbs`<h1></h1><h1></h1><h1 class="foo"></h1><h1></h1><h1></h1><h1></h1><h1></h1><h1></h1><h1></h1><h1></h1>`
);
assert.equal(
elementToString(this.element.querySelectorAll('h1')),
'h1, h1, h1.foo, h1, h1... (+5 more)'
);
});
});

test('strings', async function(assert) {
assert.equal(elementToString('h1'), 'h1');
assert.equal(elementToString('[data-test-foo]'), '[data-test-foo]');
});

module('HTMLElements', function() {
test('IDs', async function(assert) {
await render(hbs`<div id="map"></div>`);
assert.equal(elementToString(this.element.querySelector('div')), 'div#map');
});

test('CSS classes', async function(assert) {
await render(hbs`<div class="foo bar"></div>`);
assert.equal(elementToString(this.element.querySelector('div')), 'div.foo.bar');
});

test('attributes', async function(assert) {
await render(hbs`<input type="password">`);
assert.equal(elementToString(this.element.querySelector('input')), 'input[type="password"]');

await render(hbs`<input data-test-username>`);
assert.equal(
elementToString(this.element.querySelector('input')),
'input[data-test-username]'
);
});
});
});