From c7e6baed695bfb9da63efe1b1b9bf5ae9dcf0a2d Mon Sep 17 00:00:00 2001 From: nathan Date: Mon, 8 Nov 2021 13:40:28 -0700 Subject: [PATCH] Fix datetime didn't handle '0' as empty / blank --- api/js/etemplate/Et2Date/Et2Date.ts | 8 +- api/js/etemplate/Et2Date/test/Et2Date.test.ts | 42 +++--- .../Et2Date/test/Et2DateTime.test.ts | 125 ++++++++++++++++++ api/js/etemplate/Et2Date/test/Parser.test.ts | 23 +++- web-test-runner.config.mjs | 2 + 5 files changed, 182 insertions(+), 18 deletions(-) create mode 100644 api/js/etemplate/Et2Date/test/Et2DateTime.test.ts diff --git a/api/js/etemplate/Et2Date/Et2Date.ts b/api/js/etemplate/Et2Date/Et2Date.ts index a006cc1993..171667371e 100644 --- a/api/js/etemplate/Et2Date/Et2Date.ts +++ b/api/js/etemplate/Et2Date/Et2Date.ts @@ -144,7 +144,13 @@ export function parseTime(timeString) */ export function parseDateTime(dateTimeString) { - // First try the server format + // First try some common invalid values + if(dateTimeString === "" || dateTimeString === "0" || dateTimeString === 0) + { + return undefined; + } + + // Next try server format if(typeof dateTimeString === "string" && dateTimeString.substr(-1) === "Z" || !isNaN(dateTimeString)) { if(!isNaN(dateTimeString) && parseInt(dateTimeString) == dateTimeString) diff --git a/api/js/etemplate/Et2Date/test/Et2Date.test.ts b/api/js/etemplate/Et2Date/test/Et2Date.test.ts index 15a7e262ec..7ab15e8191 100644 --- a/api/js/etemplate/Et2Date/test/Et2Date.test.ts +++ b/api/js/etemplate/Et2Date/test/Et2Date.test.ts @@ -1,7 +1,7 @@ /** * Test file for Etemplate webComponent Date */ -import {assert, fixture} from '@open-wc/testing'; +import {assert, elementUpdated, fixture} from '@open-wc/testing'; import {Et2Date} from "../Et2Date"; import {html} from "lit-element"; import * as sinon from 'sinon'; @@ -47,19 +47,33 @@ describe("Date widget", () => assert.equal(element.querySelector("[slot='label']").textContent, "Label set"); }) - it('Readonly does not return a value', () => + it('Readonly does not return a value', async() => { element.readOnly = true; let test_time_string = '2008-09-22T12:00:00.000Z'; element.set_value(test_time_string); - // Use a Promise to wait for asychronous changes to the DOM - return Promise.resolve().then(() => - { - // Read-only widget returns null - assert.equal(element.getValue(), null); - }); + + // wait for asychronous changes to the DOM + await elementUpdated(element); + // Read-only widget returns null + assert.equal(element.getValue(), null); + }); + + it('No value shows no value', () => + { + assert.equal(element.querySelector("input").textContent, ""); + assert.equal(element.get_value(), null); + }); + + it("'0' shows no value", async() => + { + element.set_value("0"); + // wait for asychronous changes to the DOM + await elementUpdated(element); + assert.equal(element.querySelector("input").value, ""); + assert.equal(element.get_value(), null); }); const tz_list = [ @@ -78,16 +92,14 @@ describe("Date widget", () => ); let test_time_string = '2008-09-22T12:00:00.000Z'; let test_time = new Date(test_time_string); - it('Can accept a value', () => + it('Can accept a value', async() => { element.set_value(test_time_string); - // Use a Promise to wait for asychronous changes to the DOM - return Promise.resolve().then(() => - { - // Widget gives time as a string so we can send to server, but zeros the time - assert.equal(element.getValue().substr(0, 11), test_time_string.substr(0, 11)); - }); + // wait for asychronous changes to the DOM + await elementUpdated(element); + // Widget gives time as a string so we can send to server, but zeros the time + assert.equal(element.getValue().substr(0, 11), test_time_string.substr(0, 11)); }); /* Doesn't work yet diff --git a/api/js/etemplate/Et2Date/test/Et2DateTime.test.ts b/api/js/etemplate/Et2Date/test/Et2DateTime.test.ts new file mode 100644 index 0000000000..c13c5e651d --- /dev/null +++ b/api/js/etemplate/Et2Date/test/Et2DateTime.test.ts @@ -0,0 +1,125 @@ +/** + * Test file for Etemplate webComponent Date + */ +import {assert, elementUpdated, fixture, oneEvent} from '@open-wc/testing'; +import {html} from "lit-element"; +import * as sinon from 'sinon'; +import {Et2DateTime} from "../Et2DateTime"; + +describe("DateTime widget", () => +{ + // Reference to component under test + let element : Et2DateTime; + + // Setup run before each test + beforeEach(async() => + { + // Create an element to test with, and wait until it's ready + // @ts-ignore + element = await fixture(html` + + `); + + // Stub egw() + sinon.stub(element, "egw").returns({ + tooltipUnbind: () => {}, + // Image always give check mark. Use data URL to avoid having to serve an actual image + image: i => "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAxNS4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkViZW5lXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4IiB2aWV3Qm94PSIwIDAgMzIgMzIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDMyIDMyIiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBmaWxsPSIjNjk2OTY5IiBkPSJNNi45NDMsMjguNDUzDQoJYzAuOTA2LDAuNzY1LDIuMDk3LDEuMTI3LDMuMjg2LDEuMTA5YzAuNDMsMC4wMTQsMC44NTItMC4wNjgsMS4yNjUtMC4yMDdjMC42NzktMC4xOCwxLjMyOC0wLjQ1LDEuODY2LTAuOTAyTDI5LjQwMywxNC45DQoJYzEuNzcyLTEuNDk4LDEuNzcyLTMuOTI1LDAtNS40MjJjLTEuNzcyLTEuNDk3LTQuNjQ2LTEuNDk3LTYuNDE4LDBMMTAuMTE5LDIwLjM0OWwtMi4zODktMi40MjRjLTEuNDQtMS40NTctMy43NzItMS40NTctNS4yMTIsMA0KCWMtMS40MzgsMS40Ni0xLjQzOCwzLjgyNSwwLDUuMjgxQzIuNTE4LDIzLjIwNiw1LjQ3NCwyNi45NDcsNi45NDMsMjguNDUzeiIvPg0KPC9zdmc+DQo=" + }); + + // Stub global egw for preference + // @ts-ignore + window.egw = { + preference: () => 'Y-m-d' + }; + }); + + // Make sure it works + it('is defined', () => + { + assert.instanceOf(element, Et2DateTime); + }); + + it('has a label', () => + { + element.set_label("Label set"); + + assert.equal(element.querySelector("[slot='label']").textContent, "Label set"); + }) + + it('Readonly does not return a value', async() => + { + element.readOnly = true; + let test_time_string = '2008-09-22T12:00:00.000Z'; + + element.set_value(test_time_string); + + // wait for asychronous changes to the DOM + await elementUpdated(element); + + // Read-only widget returns null + assert.equal(element.getValue(), null); + }); + + it('No value shows no value', () => + { + assert.equal(element.querySelector("input").textContent, ""); + assert.equal(element.get_value(), null); + }); + + it("'0' shows no value", async() => + { + element.set_value("0"); + // wait for asychronous changes to the DOM + await elementUpdated(element); + assert.equal(element.querySelector("input").value, ""); + assert.equal(element.get_value(), null); + }); + + const tz_list = [ + {name: "America/Edmonton", offset: -600}, + {name: "UTC", offset: 0}, + {name: "Australia/Adelaide", offset: 630} + ]; + for(let tz of tz_list) + { + describe("Timezone: " + tz.name, () => + { + // TODO: Figure out how to mock timezone... + // Stub timezone offset to return a different value + let tz_offset_stub = sinon.stub(Date.prototype, "getTimezoneOffset").returns( + tz.offset + ); + let test_time_string = '2008-09-22T12:00:00.000Z'; + let test_time = new Date(test_time_string); + it('Can accept a value', async() => + { + element.set_value(test_time_string); + + // wait for asychronous changes to the DOM + await elementUpdated(element); + // Widget gives time as a string so we can send to server, but zeros the time + assert.equal(element.getValue().substr(0, 11), test_time_string.substr(0, 11)); + }); + + /* Doesn't work yet + it("Can be modified", () => + { + element.getInputNode().value = "2008-09-22"; + let event = new Event("change"); + element.getInputNode().dispatchEvent(event); + + // Use a Promise to wait for asychronous changes to the DOM + return Promise.resolve().then(() => + { + assert.equal(element.getValue(), "2008-09-22T00:00:00.000Z"); + }); + }); + + */ + + // Put timezone offset back + tz_offset_stub.restore(); + }); + } +}); \ No newline at end of file diff --git a/api/js/etemplate/Et2Date/test/Parser.test.ts b/api/js/etemplate/Et2Date/test/Parser.test.ts index 3fa8ae9f72..a900ac3244 100644 --- a/api/js/etemplate/Et2Date/test/Parser.test.ts +++ b/api/js/etemplate/Et2Date/test/Parser.test.ts @@ -54,6 +54,21 @@ describe("Date parsing", () => assert.equal(parsed.toJSON(), test_date.toJSON()); }); + + + it("Handles '0'", () => + { + let test_string = '0'; + let test_date = undefined; + + //@ts-ignore + window.egw = { + preference: () => 'Y.d.m' + }; + let parsed = parser(test_string); + + assert.equal(parsed, test_date); + }); }); @@ -82,7 +97,9 @@ describe("Time parsing", () => // Not valid, should be undefined "invalid": undefined, - "23:45 pm": undefined + "23:45 pm": undefined, + "0": undefined, + "": undefined }; for(let test_string of Object.keys(test_data)) { @@ -111,7 +128,9 @@ describe("Time parsing", () => // Not valid, should be undefined "invalid": undefined, - "23:45 pm": undefined + "23:45 pm": undefined, + "0": undefined, + "": undefined }; for(let test_string of Object.keys(test_data)) { diff --git a/web-test-runner.config.mjs b/web-test-runner.config.mjs index 75695382d9..44a2489941 100644 --- a/web-test-runner.config.mjs +++ b/web-test-runner.config.mjs @@ -4,6 +4,8 @@ * It uses "web-test-runner" to run the tests, which are written using * Mocha (https://mochajs.org/) & Chai Assertion Library (https://www.chaijs.com/api/assert/) * Playwright (https://playwright.dev/docs/intro) runs the tests in actual browsers. + * + * Trouble getting tests to run? Try manually compiling TypeScript (source & tests), that seems to help. */ import fs from 'fs';