From db94e5dd94e5876346e875b8824e22036706da23 Mon Sep 17 00:00:00 2001 From: Philipp Kewisch Date: Wed, 1 May 2024 13:52:46 +0200 Subject: [PATCH] Introduce the recurrence tester --- .gitignore | 1 + README.md | 12 ++-- eslint.config.js | 10 +++ lib/ical/recur_expansion.js | 2 +- package-lock.json | 87 ++++++++++++++++++++++ package.json | 6 +- tools/recur-tester.html | 140 ++++++++++++++++++++++++++++++++++++ tools/scriptutils.js | 9 +++ tools/validator.html | 19 +++-- 9 files changed, 267 insertions(+), 19 deletions(-) create mode 100644 tools/recur-tester.html diff --git a/.gitignore b/.gitignore index 2cbc1f16..795af590 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ bower_components dist/ docs/api/ docs/validator.html +docs/recur-tester.html tools/vzic/ tools/tzdb/ tools/libical/ diff --git a/README.md b/README.md index 98da93da..dc40eba0 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ bugfixing this library, please check if the fix can be upstreamed to libical. ## Sandbox and Validator If you want to try out ICAL.js right now, there is a -[jsfiddle](http://jsfiddle.net/kewisch/227efboL/) set up and ready to use. Read on for documentation -and example links. +[jsfiddle](http://jsfiddle.net/kewisch/227efboL/) set up and ready to use. -There is also a validator that demonstrates how to use the library in a webpage in the -[tools/](https://github.com/kewisch/ical.js/tree/main/tools) subdirectory. +The ICAL validator demonstrates how to use the library in a webpage, and helps verify iCalendar and +jCal. [Try the validator online](http://kewisch.github.io/ical.js/validator.html) -[Try the validator online](http://kewisch.github.io/ical.js/validator.html), it always uses the -latest release of ICAL.js. +The recurrence tester calculates occurrences based on a RRULE. It can be used to aid in +creating test cases for the recurrence iterator. +[Try the recurrence tester online](https://kewisch.github.io/ical.js/recur-tester.html). ## Installing diff --git a/eslint.config.js b/eslint.config.js index 5d603b2a..b865dd0a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,7 @@ import js from "@eslint/js"; import globals from "globals"; import stylistic from '@stylistic/eslint-plugin'; +import html from "eslint-plugin-html"; export default [ { @@ -374,5 +375,14 @@ export default [ rules: { "@stylistic/quote-props": ["error", "consistent-as-needed"] } + }, + { + files: ["tools/**/*.html"], + plugins: { + "@html": html + }, + languageOptions: { + globals: globals.browser + } } ]; diff --git a/lib/ical/recur_expansion.js b/lib/ical/recur_expansion.js index f5017f30..2694b3a1 100644 --- a/lib/ical/recur_expansion.js +++ b/lib/ical/recur_expansion.js @@ -210,7 +210,7 @@ class RecurExpansion { let maxTries = 500; let currentTry = 0; - while (true) { // eslint-disable-line no-constant-condition + while (true) { if (currentTry++ > maxTries) { throw new Error( 'max tries have occurred, rule may be impossible to fulfill.' diff --git a/package-lock.json b/package-lock.json index ec9a7a5a..60df53cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "chai": "^5.1.0", "clean-jsdoc-theme": "^4.2.18", "eslint": "^9.0.0", + "eslint-plugin-html": "^8.1.1", "globals": "^15.0.0", "jsdoc": "^4.0.2", "karma": "^6.4.3", @@ -5103,6 +5104,61 @@ "void-elements": "^2.0.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -5554,6 +5610,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-html": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-html/-/eslint-plugin-html-8.1.1.tgz", + "integrity": "sha512-6qmlJsc40D2m3Dn9oEH+0PAOkJhxVu0f5sVItqpCE0YWgYnyP4xCjBc3UWTHaJcY9ARkWOLIIuXLq0ndRnQOHw==", + "dev": true, + "dependencies": { + "htmlparser2": "^9.1.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/eslint-scope": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.1.tgz", @@ -6751,6 +6819,25 @@ "node": "^14.13.1 || >=16.0.0" } }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", diff --git a/package.json b/package.json index 895058ae..2600a00e 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "chai": "^5.1.0", "clean-jsdoc-theme": "^4.2.18", "eslint": "^9.0.0", + "eslint-plugin-html": "^8.1.1", "globals": "^15.0.0", "jsdoc": "^4.0.2", "karma": "^6.4.3", @@ -57,8 +58,9 @@ "build": "rollup -c", "lint": "eslint", "jsdoc": "rm -rf docs/api && jsdoc --configure jsdoc.json --verbose", - "validator": "sed -e 's#unpkg.com/ical.js#unpkg.com/ical.js@'`grep '\"version\"' package.json | cut -d '\"' -f 4`'/dist/ical.js#' tools/validator.html > docs/validator.html && echo 'Validator written to docs/validator.html'", - "ghpages": "npm run jsdoc && npm run validator" + "validator": "node tools/scriptutils.js replace-unpkg tools/validator.html docs/validator.html", + "recurtester": "node tools/scriptutils.js replace-unpkg tools/recur-tester.html docs/recur-tester.html", + "ghpages": "npm run jsdoc && npm run validator && npm run recurtester" }, "exports": { "import": "./dist/ical.min.js", diff --git a/tools/recur-tester.html b/tools/recur-tester.html new file mode 100644 index 00000000..2f75f5cc --- /dev/null +++ b/tools/recur-tester.html @@ -0,0 +1,140 @@ + + + + + + + + +
+

Recurrence Rule Tester

+

+ This tool allows you to calculate occurrences for RRULEs and prepare testcases for them. It + will use ICAL.js from https://unpkg.com/ical.js. Be sure to manually validate the + occurrences, as otherwise it wouldn't be a good test. +

+
+ + +
+
+ + +
+
+ + + + +
+
+
+

+      
+

+    
+ + diff --git a/tools/scriptutils.js b/tools/scriptutils.js index c3f7ce41..4462c645 100644 --- a/tools/scriptutils.js +++ b/tools/scriptutils.js @@ -132,6 +132,13 @@ async function get_tzdb_version() { return match[1]; } +async function replace_unpkg(input, output) { + let content = await fs.readFile(input, { encoding: "utf-8" }); + let pkg = JSON.parse(await fs.readFile(path.join(import.meta.dirname, "..", "package.json"), { encoding: "utf-8" })); + await fs.writeFile(output, content.replace(/unpkg.com\/ical.js/g, `unpkg.com/ical.js@${pkg.version}/dist/ical.js`)); + console.log(`unpkg link from ${input} updated to ${pkg.version} and written to ${output}`); +} + async function main() { switch (process.argv[2]) { case "tzdb-version": @@ -143,6 +150,8 @@ async function main() { case "performance-downloader": await performance_downloader(); break; + case "replace-unpkg": + await replace_unpkg(process.argv[3], process.argv[4]); } } main(); diff --git a/tools/validator.html b/tools/validator.html index c52f3100..8845c848 100644 --- a/tools/validator.html +++ b/tools/validator.html @@ -41,20 +41,20 @@ } window.validate = function validate() { - var duration = document.getElementById("duration"); + let duration = document.getElementById("duration"); - var data = document.getElementById("data"); + let data = document.getElementById("data"); - var error = document.getElementById("error"); - var jcalres = document.getElementById("jcal"); - var icalres = document.getElementById("ical"); + let error = document.getElementById("error"); + let jcalres = document.getElementById("jcal"); + let icalres = document.getElementById("ical"); duration.textContent = ''; error.textContent = jcalres.textContent = icalres.textContent = ""; - var beginTime = Date.now(); + let beginTime = Date.now(); - var jcal, ical, from; + let jcal, ical, from; try { jcal = JSON.parse(data.value); from = "json"; @@ -78,7 +78,7 @@ ical = jcal = null; } - var totalRuntime = Date.now() - beginTime; + let totalRuntime = Date.now() - beginTime; duration.textContent = 'Total Runtime: ' + totalRuntime + ' ms'; try { @@ -91,8 +91,7 @@ } return false; - } - + };