[gjs/ewlsh/whatwg-console: 5/5] Add Console tests
- From: Evan Welsh <ewlsh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs/ewlsh/whatwg-console: 5/5] Add Console tests
- Date: Mon, 16 Aug 2021 08:14:26 +0000 (UTC)
commit b3bbde4cb23dff9adaab8ae7008fe622f5980e5e
Author: Evan Welsh <contact evanwelsh com>
Date: Fri Aug 13 21:38:07 2021 -0700
Add Console tests
installed-tests/js/.eslintrc.yml | 1 +
installed-tests/js/matchers.js | 36 +++++-
installed-tests/js/meson.build | 1 +
installed-tests/js/testConsole.js | 251 ++++++++++++++++++++++++++++++++++++++
4 files changed, 288 insertions(+), 1 deletion(-)
---
diff --git a/installed-tests/js/.eslintrc.yml b/installed-tests/js/.eslintrc.yml
index cdf5cf9f..abc9c527 100644
--- a/installed-tests/js/.eslintrc.yml
+++ b/installed-tests/js/.eslintrc.yml
@@ -33,6 +33,7 @@ overrides:
- files:
- matchers.js
- testCairoModule.js
+ - testConsole.js
- testESModules.js
- testEncoding.js
- testGLibLogWriter.js
diff --git a/installed-tests/js/matchers.js b/installed-tests/js/matchers.js
index 6a2848f6..1e05828f 100644
--- a/installed-tests/js/matchers.js
+++ b/installed-tests/js/matchers.js
@@ -26,7 +26,41 @@ export function arrayLikeWithExactContents(elements) {
* @returns {string}
*/
jasmineToString() {
- return `${JSON.stringify(elements)}`;
+ return `<arrayLikeWithExactContents(${
+ elements.constructor.name
+ }[${JSON.stringify(Array.from(elements))}]>)`;
+ },
+ };
+}
+
+/**
+ * A jasmine asymmetric matcher which compares a given string to an
+ * array-like object of bytes. The compared bytes are decoded using
+ * TextDecoder and then compared using jasmine.stringMatching.
+ *
+ * @param {string | RegExp} text the text or regular expression to compare decoded bytes to
+ * @param {string} [encoding] the encoding of elements
+ * @returns
+ */
+export function decodedStringMatching(text, encoding = 'utf-8') {
+ const matcher = jasmine.stringMatching(text);
+
+ return {
+ /**
+ * @param {ArrayLike<number>} compareTo an array of bytes to decode and compare to
+ * @returns {boolean}
+ */
+ asymmetricMatch(compareTo) {
+ const decoder = new TextDecoder(encoding);
+ const decoded = decoder.decode(new Uint8Array(Array.from(compareTo)));
+
+ return matcher.asymmetricMatch(decoded, []);
+ },
+ /**
+ * @returns {string}
+ */
+ jasmineToString() {
+ return `<decodedStringMatching(${text})>`;
},
};
}
diff --git a/installed-tests/js/meson.build b/installed-tests/js/meson.build
index f85b9586..b42f3b20 100644
--- a/installed-tests/js/meson.build
+++ b/installed-tests/js/meson.build
@@ -241,6 +241,7 @@ endif
# minijasmine flag
modules_tests = [
+ 'Console',
'ESModules',
'Encoding',
'GLibLogWriter',
diff --git a/installed-tests/js/testConsole.js b/installed-tests/js/testConsole.js
new file mode 100644
index 00000000..a0e26b3a
--- /dev/null
+++ b/installed-tests/js/testConsole.js
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 Evan Welsh <contact evanwelsh com>
+
+// eslint-disable-next-line
+/// <reference types="jasmine" />
+
+import GLib from 'gi://GLib';
+import {DEFAULT_LOG_DOMAIN, Console} from 'console';
+
+import {decodedStringMatching} from './matchers.js';
+
+function objectContainingLogMessage(
+ message,
+ domain = DEFAULT_LOG_DOMAIN,
+ fields = {}
+) {
+ return jasmine.objectContaining({
+ MESSAGE: decodedStringMatching(message),
+ GLIB_DOMAIN: decodedStringMatching(domain),
+ ...fields,
+ });
+}
+
+describe('console', function () {
+ /** @type {jasmine.Spy<(_level: any, _fields: any) => any>} */
+ let writer_func;
+
+ /**
+ * @param {RegExp | string} message _
+ * @param {*} [logLevel] _
+ * @param {*} [domain] _
+ * @param {*} [fields] _
+ * @param {boolean} [resetCalls] whether to reset the mock calls after expecting
+ */
+ function expectLog(
+ message,
+ logLevel = GLib.LogLevelFlags.LEVEL_MESSAGE,
+ domain = DEFAULT_LOG_DOMAIN,
+ fields = {},
+ resetCalls = true
+ ) {
+ expect(writer_func).toHaveBeenCalledOnceWith(
+ logLevel,
+ objectContainingLogMessage(message, domain, fields)
+ );
+
+ if (resetCalls)
+ // Reset the calls if needed
+ writer_func.calls.reset();
+ }
+
+ beforeAll(function () {
+ writer_func = jasmine.createSpy(
+ 'Console test writer func',
+ function (level, _fields) {
+ if (level === GLib.LogLevelFlags.ERROR)
+ return GLib.LogWriterOutput.UNHANDLED;
+
+ return GLib.LogWriterOutput.HANDLED;
+ }
+ );
+
+ writer_func.and.callThrough();
+
+ // @ts-expect-error The existing binding doesn't accept any parameters because
+ // it is a raw pointer.
+ GLib.log_set_writer_func(writer_func);
+ });
+
+ beforeEach(function () {
+ writer_func.calls.reset();
+ });
+
+ it('has correct object tag', function () {
+ expect(console.toString()).toBe('[object console]');
+ });
+
+ it('logs a message', function () {
+ console.log('a log');
+
+ expect(writer_func).toHaveBeenCalledWith(
+ GLib.LogLevelFlags.LEVEL_MESSAGE,
+ objectContainingLogMessage('a log')
+ );
+ });
+
+ it('logs a warning', function () {
+ console.warn('a warning');
+
+ expect(writer_func).toHaveBeenCalledWith(
+ GLib.LogLevelFlags.LEVEL_WARNING,
+ objectContainingLogMessage('a warning')
+ );
+ });
+
+ it('logs an informative message', function () {
+ console.info('an informative message');
+
+ expect(writer_func).toHaveBeenCalledWith(
+ GLib.LogLevelFlags.LEVEL_INFO,
+ objectContainingLogMessage('an informative message')
+ );
+ });
+
+ describe('console.clear()', function () {
+ it('clear can be called.', function () {
+ console.clear();
+ });
+
+ it('clear resets indentation.', function () {
+ console.group('a group');
+ expectLog('a group');
+ console.log('a log');
+ expectLog(' a log');
+ console.clear();
+ console.log('a log');
+ expectLog('a log');
+ });
+ });
+
+ // %s - string
+ // %d or %i - integer
+ // %f - float
+ // %o - "optimal" object formatting
+ // %O - "generic" object formatting
+ // %c - "CSS" formatting (unimplemented by GJS)
+ describe('string replacement', function () {
+ const functions = {
+ log: GLib.LogLevelFlags.LEVEL_MESSAGE,
+ warn: GLib.LogLevelFlags.LEVEL_WARNING,
+ info: GLib.LogLevelFlags.LEVEL_INFO,
+ error: GLib.LogLevelFlags.LEVEL_CRITICAL,
+ };
+
+ Object.entries(functions).forEach(([fn, level]) => {
+ it(`console.${fn}() supports %s`, function () {
+ console[fn]('Does this %s substitute correctly?', 'modifier');
+ expectLog('Does this modifier substitute correctly?', level);
+ });
+
+ it(`console.${fn}() supports %d`, function () {
+ console[fn]('Does this %d substitute correctly?', 10);
+ expectLog('Does this 10 substitute correctly?', level);
+ });
+
+ it(`console.${fn}() supports %i`, function () {
+ console[fn]('Does this %i substitute correctly?', 26);
+ expectLog('Does this 26 substitute correctly?', level);
+ });
+
+ it(`console.${fn}() supports %f`, function () {
+ console[fn]('Does this %f substitute correctly?', 27.56331);
+ expectLog('Does this 27.56331 substitute correctly?', level);
+ });
+
+ it(`console.${fn}() supports %o`, function () {
+ console[fn]('Does this %o substitute correctly?', new Error());
+ expectLog(/Does this Error\n.*substitute correctly\?/s, level);
+ });
+
+ it(`console.${fn}() supports %O`, function () {
+ console[fn]('Does this %O substitute correctly?', new Error());
+ expectLog('Does this {} substitute correctly?', level);
+ });
+
+ it(`console.${fn}() ignores %c`, function () {
+ console[fn]('Does this %c substitute correctly?', 'modifier');
+ expectLog('Does this substitute correctly?', level);
+ });
+
+ it(`console.${fn}() supports mixing substitutions`, function () {
+ console[fn](
+ 'Does this %s and the %f substitute correctly alongside %d?',
+ 'string',
+ 3.14,
+ 14
+ );
+ expectLog(
+ 'Does this string and the 3.14 substitute correctly alongside 14?',
+ level
+ );
+ });
+
+ it(`console.${fn}() supports invalid numbers`, function () {
+ console[fn](
+ 'Does this support parsing %i incorrectly?',
+ 'a string'
+ );
+ expectLog('Does this support parsing NaN incorrectly?', level);
+ });
+
+ it(`console.${fn}() supports missing substitutions`, function () {
+ console[fn]('Does this support a missing %s substitution?');
+ expectLog(
+ 'Does this support a missing %s substitution?',
+ level
+ );
+ });
+ });
+ });
+
+ describe('console.time()', function () {
+ it('ends correctly', function (done) {
+ console.time('testing time');
+
+ // console.time logs nothing.
+ expect(writer_func).not.toHaveBeenCalled();
+
+ setTimeout(() => {
+ console.timeLog('testing time');
+
+ expectLog(/testing time: (.*)ms/);
+
+ console.timeEnd('testing time');
+
+ expectLog(/testing time: (.*)ms/);
+
+ console.timeLog('testing time');
+
+ expectLog(
+ "No time log found for label: 'testing time'.",
+ GLib.LogLevelFlags.LEVEL_WARNING
+ );
+
+ done();
+ }, 10);
+ });
+
+ it("doesn't log initially", function (done) {
+ console.time('testing time');
+
+ // console.time logs nothing.
+ expect(writer_func).not.toHaveBeenCalled();
+
+ setTimeout(() => {
+ console.timeEnd('testing time');
+ expectLog(/testing time: (.*)ms/);
+
+ done();
+ }, 10);
+ });
+ });
+});
+
+describe('Console constructor', function () {
+ const console = new Console();
+
+ it('has correct object tag', function () {
+ expect(console.toString()).toBe('[object Console]');
+ });
+});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]