Rename files to mjs and get Jasmine testing inside NodeJS working [#32]

This commit is contained in:
David Evans 2018-04-21 16:34:09 +01:00
parent 369eb15390
commit 506aecc445
120 changed files with 756 additions and 411 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.DS_Store
xcuserdata
xcshareddata
ephemeral

View File

@ -7,18 +7,18 @@
<FileRef
location = "container:scripts">
</FileRef>
<FileRef
location = "container:spec">
</FileRef>
<FileRef
location = "container:styles">
</FileRef>
<FileRef
location = "container:web">
</FileRef>
<FileRef
location = "container:.gitignore">
</FileRef>
<FileRef
location = "container:.eslintrc.js">
</FileRef>
<FileRef
location = "group:rollup.config.js">
</FileRef>
<FileRef
location = "container:editor-dev.htm">
</FileRef>

View File

@ -40,10 +40,10 @@ web modules unless a flag is set (FireFox 60 will support them fully).
The editor and library page do not require web modules, so should have
wider support.
To run the linter, run the command:
To run the non-browser tests and linter, run the command:
```shell
npm run lint;
npm test;
```
And to rebuild the minified sources, run:
@ -52,7 +52,18 @@ And to rebuild the minified sources, run:
npm run minify;
```
Currently there are no command-line tests; only the browser tests.
## Commands
The available commands are:
* `npm start`: runs a webserver on
[localhost:8080](http://localhost:8080)
* `npm test`: runs the `unit-test` and `lint` commands
* `npm run unit-test`: runs non-browser-based unit tests in NodeJS
* `npm run lint`: runs the linter against all source and test files
* `npm run minify`: runs the `minify-lib` and `minify-web` commands
* `npm run minify-lib`: minifies the library code in `/lib`
* `npm run minify-web`: minifies the web code in `/weblib`
## Project Structure
@ -78,18 +89,19 @@ Useful helpers can also be found in `/scripts/core/*` and
`/scripts/svg/*`.
The live editor (index.htm & editor-dev.htm) uses the source in
`/scripts/editor.js` and `/scripts/interface/*`. Other pages use
sources in the root of `/scripts` as their entry-points.
`/web/editor.mjs` and `/web/interface/*`. Other pages use sources in
the root of `/web` as their entry-points.
## Testing
The testing library used here is [Jasmine](https://jasmine.github.io/).
All test files follow the naming convention of `<filename>_spec.js`,
and must be listed in `/scripts/spec.js`. Linting automatically applies
to all files with a `.js` extension.
All test files follow the naming convention of `<filename>_spec.mjs`
(commandline and browser) or `_webspec.mjs` (browser-only). Browser
tests must be listed in `/spec/support/browser_specs.mjs`. Linting
automatically applies to all files with a `.js` or `.mjs` extension.
You can run the tests by opening `test.htm` in a browser.
You can run the browser tests by opening `test.htm` in a browser.
The current state of automated testing is:
@ -97,12 +109,12 @@ The current state of automated testing is:
* `Parser` and `Generator` stages have a good level of testing
* Rendering methods (SVG generation) have a minimal level of testing;
there are some high-level tests in
`/scripts/sequence/SequenceDiagram_spec.js`, and a series of image
comparison tests in `/scripts/sequence/Readme_spec.js` (testing that
`/scripts/sequence/SequenceDiagram_spec.mjs`, and a series of image
comparison tests in `/scripts/sequence/Readme_spec.mjs` (testing that
the readme screenshots roughly match the current behaviour). Finally
`/scripts/sequence/SequenceDiagram_visual_spec.js` uses coarse image
`/scripts/sequence/SequenceDiagram_visual_spec.mjs` uses coarse image
comparison to test components and interactions using baseline SVGs
from `test-images`.
from `spec/images`.
* The editor has a minimal level of testing.
If you suspect a failing test is not related to your changes, you can
@ -129,7 +141,7 @@ polyfils are included.
### Testing & Linting
Ensure that all unit tests are passing and files pass linting. This can
be done by opening `test.htm` in a browser and running `npm run lint`
be done by opening `test.htm` in a browser and running `npm test`
in a command window. At a minimum, you should ensure that tests are
passing in Google Chrome, but testing in other supported browsers is
even better.
@ -150,7 +162,7 @@ npm run minify;
```
This will update the files in `/lib` and `/weblib`. The minified code
is a self-contained copy of the `/scripts/sequence/SequenceDiagram.js`
is a self-contained copy of the `/scripts/sequence/SequenceDiagram.mjs`
script, with some boiler-plate added to allow loading into a page in a
variety of ways.

View File

@ -3,7 +3,7 @@ reference it with #issue-number.
Checklist: (tick with [x])
- [ ] Any new spec files are listed in `scripts/specs.js`.
- [ ] Any new spec files are listed in `spec/support/browser_specs.mjs`.
- [ ] Tests are passing in Google Chrome.
- [ ] Linting is passing (`npm run lint`)
- [ ] No dead code is left behind.

View File

@ -67,7 +67,7 @@
data-integrity="sha256-HYX1RusN7a369vYuOd1mGvxLcNL4z/MihkahAI2CH8k="
>
<script src="scripts/editor.js" type="module"></script>
<script src="web/editor.mjs" type="module"></script>
</head>
<body>

View File

@ -1239,8 +1239,19 @@
}
}
const nodejs = (typeof window === 'undefined');
// Thanks, https://stackoverflow.com/a/23522755/1180785
const safari = (/^((?!chrome|android).)*safari/i).test(navigator.userAgent);
const safari = (
!nodejs &&
(/^((?!chrome|android).)*safari/i).test(window.navigator.userAgent)
);
// Thanks, https://stackoverflow.com/a/9851769/1180785
const firefox = (
!nodejs &&
typeof window.InstallTrigger !== 'undefined'
);
class Exporter {
constructor() {
@ -6620,9 +6631,6 @@
}
}
// Thanks, https://stackoverflow.com/a/9851769/1180785
const firefox = (typeof window.InstallTrigger !== 'undefined');
function merge(state, newState) {
for(const k in state) {
if(Object.prototype.hasOwnProperty.call(state, k)) {
@ -7115,16 +7123,16 @@
return this.dom.el(tag, namespace);
}
box(attrs, position) {
return this.el('rect').attrs(attrs).attrs(position);
box(attrs, {height, width, x, y}) {
return this.el('rect').attrs(attrs).attrs({height, width, x, y});
}
boxFactory(attrs) {
return this.box.bind(this, attrs);
}
line(attrs, position) {
return this.el('line').attrs(attrs).attrs(position);
line(attrs, {x1, x2, y1, y2}) {
return this.el('line').attrs(attrs).attrs({x1, x2, y1, y2});
}
lineFactory(attrs) {
@ -7160,11 +7168,11 @@
return this.cross.bind(this, attrs);
}
note(attrs, flickAttrs, position) {
const x0 = position.x;
const x1 = position.x + position.width;
const y0 = position.y;
const y1 = position.y + position.height;
note(attrs, flickAttrs, {height, width, x, y}) {
const x0 = x;
const x1 = x + width;
const y0 = y;
const y1 = y + height;
const flick = 7;
return this.el('g').add(
@ -7191,13 +7199,13 @@
return this.note.bind(this, attrs, flickAttrs);
}
formattedText(attrs = {}, formatted = [], position = {}) {
formattedText(attrs = {}, formatted = [], {x, y} = {}) {
const container = this.el('g');
const txt = new SVGTextBlock(container, this, {
attrs,
formatted,
x: position.x,
y: position.y,
x,
y,
});
return Object.assign(container, {
set: (state) => txt.set(state),
@ -9544,7 +9552,9 @@
function pickDocument(container) {
if(container) {
return container.ownerDocument;
return container.ownerDocument || null;
} else if(typeof window === 'undefined') {
return null;
} else {
return window.document;
}
@ -9921,6 +9931,10 @@
convert(els);
}
function getDefaultThemeNames() {
return themes.map((theme) => theme.name);
}
Object.assign(SequenceDiagram, {
Exporter,
Generator,
@ -9930,6 +9944,7 @@
convert,
convertAll,
extractCodeFromSVG,
getDefaultThemeNames,
registerCodeMirrorMode,
renderAll,
themes,

File diff suppressed because one or more lines are too long

194
package-lock.json generated
View File

@ -111,6 +111,12 @@
"integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
"dev": true
},
"arr-union": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true
},
"array-union": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@ -144,6 +150,12 @@
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true
},
"async-array-reduce": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/async-array-reduce/-/async-array-reduce-0.2.1.tgz",
"integrity": "sha1-yL4BCitc0A3qlsgRFgNGk9/dgtE=",
"dev": true
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -580,6 +592,24 @@
"fill-range": "2.2.3"
}
},
"expand-tilde": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz",
"integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=",
"dev": true,
"requires": {
"os-homedir": "1.0.2"
}
},
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "0.1.1"
}
},
"external-editor": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
@ -683,6 +713,12 @@
"for-in": "1.0.2"
}
},
"fs-exists-sync": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz",
"integrity": "sha1-mC1ok6+RjnLQjeyehnP/K1qNat0=",
"dev": true
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -728,6 +764,28 @@
"is-glob": "2.0.1"
}
},
"global-modules": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/global-modules/-/global-modules-0.2.3.tgz",
"integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=",
"dev": true,
"requires": {
"global-prefix": "0.1.5",
"is-windows": "0.2.0"
}
},
"global-prefix": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-0.1.5.tgz",
"integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=",
"dev": true,
"requires": {
"homedir-polyfill": "1.0.1",
"ini": "1.3.5",
"is-windows": "0.2.0",
"which": "1.3.0"
}
},
"globals": {
"version": "11.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz",
@ -769,12 +827,30 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-glob": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/has-glob/-/has-glob-0.1.1.tgz",
"integrity": "sha1-omHEwqbGZ+DHe3AKfyl8Oe86pYk=",
"dev": true,
"requires": {
"is-glob": "2.0.1"
}
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"homedir-polyfill": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz",
"integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=",
"dev": true,
"requires": {
"parse-passwd": "1.0.0"
}
},
"http-proxy": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz",
@ -838,6 +914,12 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
"dev": true
},
"inquirer": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
@ -980,6 +1062,18 @@
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true
},
"is-valid-glob": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-0.3.0.tgz",
"integrity": "sha1-1LVcafUYhvm2XHDWwmItN+KfSP4=",
"dev": true
},
"is-windows": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz",
"integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw=",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -1001,6 +1095,22 @@
"isarray": "1.0.0"
}
},
"jasmine": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.1.0.tgz",
"integrity": "sha1-K9Wf1+xuwOistk4J9Fpo7SrRlSo=",
"dev": true,
"requires": {
"glob": "7.1.2",
"jasmine-core": "3.1.0"
}
},
"jasmine-core": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.1.0.tgz",
"integrity": "sha1-pHheE11d9lAk38kiSVPfWFvSdmw=",
"dev": true
},
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
@ -1038,6 +1148,15 @@
"is-buffer": "1.1.6"
}
},
"lazy-cache": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz",
"integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=",
"dev": true,
"requires": {
"set-getter": "0.1.0"
}
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@ -1070,6 +1189,23 @@
"yallist": "2.1.2"
}
},
"matched": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/matched/-/matched-0.4.4.tgz",
"integrity": "sha1-Vte36xgDPwz5vFLrIJD6x9weifo=",
"dev": true,
"requires": {
"arr-union": "3.1.0",
"async-array-reduce": "0.2.1",
"extend-shallow": "2.0.1",
"fs-exists-sync": "0.1.0",
"glob": "7.1.2",
"has-glob": "0.1.1",
"is-valid-glob": "0.3.0",
"lazy-cache": "2.0.2",
"resolve-dir": "0.1.1"
}
},
"micromatch": {
"version": "2.3.11",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz",
@ -1242,6 +1378,12 @@
}
}
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@ -1266,6 +1408,12 @@
"integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=",
"dev": true
},
"parse-passwd": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
"dev": true
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1488,6 +1636,16 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true
},
"resolve-dir": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-0.1.1.tgz",
"integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=",
"dev": true,
"requires": {
"expand-tilde": "1.2.2",
"global-modules": "0.2.3"
}
},
"resolve-from": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
@ -1538,6 +1696,15 @@
"integrity": "sha512-MlxPQTkMtiRUtyhIJ7FpBvTzWtar8eFBA+V7/J6Deg9fSgIIHwL6bJKK1Wl1uWSWtOrWhOmtsMwb9F6aagP/Pg==",
"dev": true
},
"rollup-plugin-multi-entry": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/rollup-plugin-multi-entry/-/rollup-plugin-multi-entry-2.0.2.tgz",
"integrity": "sha512-TY72fCVJvcEAQBpBzkXykoYQx2fz0B20EVtcbh0WZaYr5eBu3U1dRPzgMt6aO8MePWWOdcmgoBtG6PhmYJr4Ew==",
"dev": true,
"requires": {
"matched": "0.4.4"
}
},
"rollup-pluginutils": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz",
@ -1590,6 +1757,15 @@
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
},
"set-getter": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz",
"integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=",
"dev": true,
"requires": {
"to-object-path": "0.3.0"
}
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -1626,6 +1802,15 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.4.tgz",
"integrity": "sha512-PETSPG6BjY1AHs2t64vS2aqAgu6dMIMXJULWFBGbh2Gr8nVLbCFDo6i/RMMvviIQ2h1Z8+5gQhVKSn2je9nmdg==",
"dev": true,
"requires": {
"source-map": "0.6.1"
}
},
"sourcemap-codec": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz",
@ -1727,6 +1912,15 @@
"os-tmpdir": "1.0.2"
}
},
"to-object-path": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
"integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
"dev": true,
"requires": {
"kind-of": "3.2.2"
}
},
"type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",

View File

@ -15,19 +15,26 @@
"lib/sequence-diagram.js"
],
"main": "lib/sequence-diagram",
"module": "source/standalone",
"module": "scripts/standalone",
"scripts": {
"lint": "eslint scripts --ext .js --ignore-pattern '!.eslintrc.js' --ignore-pattern '*FontData.js'",
"minify": "rollup --config rollup.config.js && uglifyjs --compress --mangle --warn --output lib/sequence-diagram.min.js -- lib/sequence-diagram.js && uglifyjs --compress --mangle --warn --output weblib/editor.min.js -- weblib/editor.js",
"start": "http-server"
"lint": "eslint . --config spec/support/eslintrc.js --ignore-path spec/support/eslintignore --ext .js --ext .mjs",
"minify-lib": "rollup --config scripts/rollup.config.js && uglifyjs --compress --mangle --warn --output lib/sequence-diagram.min.js -- lib/sequence-diagram.js",
"minify-web": "rollup --config web/rollup.config.js && uglifyjs --compress --mangle --warn --output weblib/editor.min.js -- weblib/editor.js",
"minify": "npm run minify-lib && npm run minify-web",
"start": "http-server",
"test": "npm run unit-test && npm run lint",
"unit-test": "rollup --config spec/support/rollup.config.js && node -r source-map-support/register node_modules/.bin/jasmine --config=spec/support/jasmine.json"
},
"devDependencies": {
"eslint": "^4.19.1",
"eslint-plugin-jasmine": "^2.9.3",
"http-server": "^0.10.0",
"jasmine": "^3.1.0",
"requirejs": "2.3.5",
"rollup": "^0.57.1",
"rollup-plugin-hypothetical": "^2.1.0",
"rollup-plugin-multi-entry": "^2.0.2",
"source-map-support": "^0.5.4",
"uglify-es": "^3.1.10"
}
}

View File

@ -19,7 +19,7 @@
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<link rel="stylesheet" href="styles/readmeImages.css">
<script src="scripts/readmeImages.js" type="module"></script>
<script src="web/readmeImages.mjs" type="module"></script>
</head>
<body>

View File

@ -7,7 +7,7 @@ import {
mergeSets,
remove,
removeAll,
} from './ArrayUtilities.js';
} from './ArrayUtilities.mjs';
describe('ArrayUtilities', () => {
function ignoreCase(a, b) {

View File

@ -1,4 +1,4 @@
import EventObject from './EventObject.js';
import EventObject from './EventObject.mjs';
describe('EventObject', () => {
let o = null;

View File

@ -1,4 +1,4 @@
import Random from './Random.js';
import Random from './Random.mjs';
describe('Random', () => {
let random = null;

15
scripts/core/browser.mjs Normal file
View File

@ -0,0 +1,15 @@
export const nodejs = (typeof window === 'undefined');
export const headless = nodejs;
// Thanks, https://stackoverflow.com/a/23522755/1180785
export const safari = (
!nodejs &&
(/^((?!chrome|android).)*safari/i).test(window.navigator.userAgent)
);
// Thanks, https://stackoverflow.com/a/9851769/1180785
export const firefox = (
!nodejs &&
typeof window.InstallTrigger !== 'undefined'
);

View File

@ -104,6 +104,39 @@ class ElementNode {
return false;
}
getElementsByTagName(tag) {
const result = [];
this.traverseDescendants((o) => {
if(o.tagName === tag) {
result.push(o);
}
});
return result;
}
getElementsByClassName(className) {
const result = [];
const check = ' ' + className + ' ';
this.traverseDescendants((o) => {
const cls = ' ' + (o.getAttribute('class') || '') + ' ';
if(cls.indexOf(check) !== -1) {
result.push(o);
}
});
return result;
}
traverseDescendants(fn) {
if(fn(this) === false) {
return;
}
for(const child of this.childNodes) {
if(child.traverseDescendants) {
child.traverseDescendants(fn);
}
}
}
get firstChild() {
return this.childNodes[0] || null;
}
@ -195,7 +228,7 @@ class ElementNode {
}
}
export default class VirtualDocument {
export class VirtualDocument {
createElement(tag) {
return new ElementNode(this, tag, '');
}
@ -208,3 +241,9 @@ export default class VirtualDocument {
return new TextNode(content);
}
}
export class Event {
constructor(type) {
this.type = type;
}
}

View File

@ -1,4 +1,4 @@
import VirtualDocument from './VirtualDocument.js';
import {Event, VirtualDocument} from './VirtualDocument.mjs';
describe('VirtualDocument', () => {
const doc = new VirtualDocument();

View File

@ -1,4 +1,4 @@
import ImageRegion from './ImageRegion.js';
import ImageRegion from './ImageRegion.mjs';
export function makeGaussianKernel(size) {
const sz = Math.ceil(size * 3);

View File

@ -1,5 +1,5 @@
import ImageRegion from './ImageRegion.js';
import {blur2D} from './Blur.js';
import ImageRegion from './ImageRegion.mjs';
import {blur2D} from './Blur.mjs';
describe('Blur', () => {
const PRECISION = 0.01;

View File

@ -1,4 +1,4 @@
import ImageRegion from './ImageRegion.js';
import ImageRegion from './ImageRegion.mjs';
function compose(input1, input2, fn, {target = null} = {}) {
input1.checkCompatible(input2);

View File

@ -1,5 +1,5 @@
import ImageRegion from './ImageRegion.js';
import {subtract} from './Composition.js';
import ImageRegion from './ImageRegion.mjs';
import {subtract} from './Composition.mjs';
describe('Composition', () => {
let inputA = null;

View File

@ -1,9 +1,13 @@
/* eslint-disable max-statements */
import ImageRegion from './ImageRegion.js';
import ImageRegion from './ImageRegion.mjs';
import {nodejs} from '../core/browser.mjs';
describe('ImageRegion', () => {
function makeCanvas(w, h) {
if(nodejs) {
return pending('No canvas support in NodeJS');
}
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;

View File

@ -1,6 +1,7 @@
import './Blur.js';
import './Composition.js';
import ImageRegion from './ImageRegion.js';
import './Blur.mjs';
import './Composition.mjs';
import ImageRegion from './ImageRegion.mjs';
import {headless} from '../core/browser.mjs';
function getThresholds({
pixelThresh = 2,
@ -74,20 +75,22 @@ export const matchers = {
return {pass: true};
}
document.body.appendChild(makeImageComparison(
actual,
expected,
options,
'Image comparison (expected similar)'
));
const details = (options.details ?
let message = 'Expected images to be similar';
if(!headless) {
document.body.appendChild(makeImageComparison(
actual,
expected,
options,
'Image comparison (expected similar)'
));
message += ' (see below for comparison)';
}
message += '.' + (options.details ?
' Details: ' + options.details : ''
);
return {
message: 'Expected images to be similar ' +
'(see below for comparison).' + details,
message,
pass: false,
};
},
@ -97,20 +100,22 @@ export const matchers = {
return {pass: true};
}
document.body.appendChild(makeImageComparison(
actual,
expected,
options,
'Image comparison (expected different)'
));
const details = (options.details ?
let message = 'Expected images to differ';
if(!headless) {
document.body.appendChild(makeImageComparison(
actual,
expected,
options,
'Image comparison (expected different)'
));
message += ' (see below for comparison)';
}
message += '.' + (options.details ?
' Details: ' + options.details : ''
);
return {
message: 'Expected images to differ ' +
'(see below for comparison).' + details,
message,
pass: false,
};
},

View File

@ -1,5 +1,5 @@
import {isSimilar, matchers} from './ImageSimilarity.js';
import ImageRegion from './ImageRegion.js';
import {isSimilar, matchers} from './ImageSimilarity.mjs';
import ImageRegion from './ImageRegion.mjs';
describe('ImageSimilarity', () => {
let inputA = null;

View File

@ -1,26 +0,0 @@
import ComponentsLibrary from './ComponentsLibrary.js';
import SequenceDiagram from '../sequence/SequenceDiagram.js';
const themes = new SequenceDiagram().getThemeNames().slice(1);
function checkSample(src) {
it('renders without error', () => {
expect(() => new SequenceDiagram(src)).not.toThrow();
});
themes.forEach((themeName) => {
it('renders without error in ' + themeName + ' theme', () => {
expect(() => new SequenceDiagram(
'theme ' + themeName + '\n' + src
)).not.toThrow();
});
});
}
describe('Components Library', () => {
ComponentsLibrary.forEach(({title, code, preview}) => {
describe(title, () => {
checkSample(preview || code);
});
});
});

View File

@ -1,31 +0,0 @@
// Jasmine test configuration.
// See specs.js for the list of spec files
jasmine.executeAllTests = window.onload;
window.onload = null;
const matchers = {
toBeNear: () => ({
compare: (actual, expected, range) => {
if(
typeof expected !== 'number' ||
typeof range !== 'number' ||
range < 0
) {
throw new Error(
'Invalid toBeNear(' + expected + ',' + range + ')'
);
}
if(typeof actual !== 'number') {
throw new Error('Expected a number, got ' + actual);
}
return {
pass: Math.abs(actual - expected) <= range,
};
},
}),
};
beforeAll(() => {
jasmine.addMatchers(matchers);
});

10
scripts/rollup.config.js Normal file
View File

@ -0,0 +1,10 @@
export default [
{
input: 'scripts/standalone.mjs',
output: {
file: 'lib/sequence-diagram.js',
format: 'iife',
name: 'SequenceDiagram',
},
},
];

View File

@ -1,6 +1,6 @@
/* eslint-disable complexity */ // Temporary ignore while switching linter
import {last, mergeSets} from '../core/ArrayUtilities.js';
import {last, mergeSets} from '../core/ArrayUtilities.mjs';
const TRIMMER = /^([ \t]*)(.*)$/;
const SQUASH = {

View File

@ -2,7 +2,7 @@
/* eslint-disable sort-keys */ // Maybe later
/* eslint-disable complexity */ // Temporary ignore while switching linter
import {flatMap, last, mergeSets} from '../core/ArrayUtilities.js';
import {flatMap, last, mergeSets} from '../core/ArrayUtilities.mjs';
const CM_ERROR = {type: 'error line-error', suggest: false, then: {'': 0}};

View File

@ -2,7 +2,7 @@
/* eslint-disable max-statements */
/* eslint-disable sort-keys */ // Maybe later
import SequenceDiagram from './SequenceDiagram.js';
import SequenceDiagram from './SequenceDiagram.mjs';
const CM = window.CodeMirror;

View File

@ -1,5 +1,4 @@
// Thanks, https://stackoverflow.com/a/23522755/1180785
const safari = (/^((?!chrome|android).)*safari/i).test(navigator.userAgent);
import {safari} from '../core/browser.mjs';
export default class Exporter {
constructor() {

View File

@ -10,7 +10,7 @@ import {
mergeSets,
remove,
removeAll,
} from '../core/ArrayUtilities.js';
} from '../core/ArrayUtilities.mjs';
class AgentState {
constructor({

View File

@ -2,7 +2,7 @@
/* eslint-disable max-statements */
/* eslint-disable sort-keys */ // Maybe later
import Generator from './Generator.js';
import Generator from './Generator.mjs';
describe('Sequence Generator', () => {
const generator = new Generator();

View File

@ -1,4 +1,4 @@
import parser from './LabelPatternParser.js';
import parser from './LabelPatternParser.mjs';
describe('Label Pattern Parser', () => {
it('converts simple text', () => {

View File

@ -1,8 +1,8 @@
/* eslint-disable sort-keys */ // Maybe later
import {dom, textSizerFactory} from '../stubs/TestDOM.js';
import SVG from '../svg/SVG.js';
import parser from './MarkdownParser.js';
import {dom, textSizerFactory} from '../../spec/stubs/TestDOM.mjs';
import SVG from '../svg/SVG.mjs';
import parser from './MarkdownParser.mjs';
describe('Markdown Parser', () => {
it('converts simple text', () => {

View File

@ -3,10 +3,10 @@
/* eslint-disable complexity */ // Temporary ignore while switching linter
/* eslint-disable no-param-reassign */ // Also temporary
import {combine, last} from '../core/ArrayUtilities.js';
import Tokeniser from './Tokeniser.js';
import labelPatternParser from './LabelPatternParser.js';
import markdownParser from './MarkdownParser.js';
import {combine, last} from '../core/ArrayUtilities.mjs';
import Tokeniser from './Tokeniser.mjs';
import labelPatternParser from './LabelPatternParser.mjs';
import markdownParser from './MarkdownParser.mjs';
const BLOCK_TYPES = {
'if': {

View File

@ -2,7 +2,7 @@
/* eslint-disable max-statements */
/* eslint-disable sort-keys */ // Maybe later
import Parser from './Parser.js';
import Parser from './Parser.mjs';
describe('Sequence Parser', () => {
const parser = new Parser();

View File

@ -1,6 +1,6 @@
import ImageRegion from '../image/ImageRegion.js';
import SequenceDiagram from './SequenceDiagram.js';
import {matchers} from '../image/ImageSimilarity.js';
import ImageRegion from '../image/ImageRegion.mjs';
import SequenceDiagram from './SequenceDiagram.mjs';
import {matchers} from '../image/ImageSimilarity.mjs';
const RESOLUTION = 4;

View File

@ -1,21 +1,21 @@
/* eslint-disable max-lines */
import './components/AgentCap.js';
import './components/AgentHighlight.js';
import './components/Block.js';
import './components/Connect.js';
import './components/Divider.js';
import './components/Marker.js';
import './components/Note.js';
import './components/Parallel.js';
import './components/AgentCap.mjs';
import './components/AgentHighlight.mjs';
import './components/Block.mjs';
import './components/Connect.mjs';
import './components/Divider.mjs';
import './components/Marker.mjs';
import './components/Note.mjs';
import './components/Parallel.mjs';
import {
cleanRenderPreResult,
getComponents,
} from './components/BaseComponent.js';
import DOMWrapper from '../core/DOMWrapper.js';
import EventObject from '../core/EventObject.js';
import SVG from '../svg/SVG.js';
import {mergeSets} from '../core/ArrayUtilities.js';
} from './components/BaseComponent.mjs';
import DOMWrapper from '../core/DOMWrapper.mjs';
import EventObject from '../core/EventObject.mjs';
import SVG from '../svg/SVG.mjs';
import {mergeSets} from '../core/ArrayUtilities.mjs';
function findExtremes(agentInfos, agentIDs) {
let min = null;

View File

@ -1,21 +1,18 @@
/* eslint-disable sort-keys */ // Maybe later
import {Factory as BasicThemeFactory} from './themes/Basic.js';
import Renderer from './Renderer.js';
import {VirtualDocument, textSizerFactory} from '../../spec/stubs/TestDOM.mjs';
import {Factory as BasicThemeFactory} from './themes/Basic.mjs';
import Renderer from './Renderer.mjs';
describe('Sequence Renderer', () => {
let renderer = null;
beforeEach(() => {
renderer = new Renderer({
document: new VirtualDocument(),
themes: [new BasicThemeFactory()],
document: window.document,
textSizerFactory,
});
document.body.appendChild(renderer.dom());
});
afterEach(() => {
document.body.removeChild(renderer.dom());
});
describe('.dom', () => {

View File

@ -1,13 +1,13 @@
import {Factory as BasicThemeFactory} from './themes/Basic.js';
import {Factory as ChunkyThemeFactory} from './themes/Chunky.js';
import EventObject from '../core/EventObject.js';
import Exporter from './Exporter.js';
import Generator from './Generator.js';
import {Factory as MonospaceThemeFactory} from './themes/Monospace.js';
import Parser from './Parser.js';
import Renderer from './Renderer.js';
import {Factory as SketchThemeFactory} from './themes/Sketch.js';
import {getHints} from './CodeMirrorHints.js';
import {Factory as BasicThemeFactory} from './themes/Basic.mjs';
import {Factory as ChunkyThemeFactory} from './themes/Chunky.mjs';
import EventObject from '../core/EventObject.mjs';
import Exporter from './Exporter.mjs';
import Generator from './Generator.mjs';
import {Factory as MonospaceThemeFactory} from './themes/Monospace.mjs';
import Parser from './Parser.mjs';
import Renderer from './Renderer.mjs';
import {Factory as SketchThemeFactory} from './themes/Sketch.mjs';
import {getHints} from './CodeMirrorHints.mjs';
const themes = [
new BasicThemeFactory(),
@ -65,7 +65,9 @@ function renderAll(diagrams) {
function pickDocument(container) {
if(container) {
return container.ownerDocument;
return container.ownerDocument || null;
} else if(typeof window === 'undefined') {
return null;
} else {
return window.document;
}
@ -442,6 +444,10 @@ function convertAll(root = null, className = 'sequence-diagram') {
convert(els);
}
function getDefaultThemeNames() {
return themes.map((theme) => theme.name);
}
Object.assign(SequenceDiagram, {
Exporter,
Generator,
@ -451,6 +457,7 @@ Object.assign(SequenceDiagram, {
convert,
convertAll,
extractCodeFromSVG,
getDefaultThemeNames,
registerCodeMirrorMode,
renderAll,
themes,

View File

@ -1,9 +1,10 @@
import Exporter from './Exporter.js';
import Generator from './Generator.js';
import Parser from './Parser.js';
import Renderer from './Renderer.js';
import SequenceDiagram from './SequenceDiagram.js';
import {textSizerFactory} from '../stubs/TestDOM.js';
import {VirtualDocument, textSizerFactory} from '../../spec/stubs/TestDOM.mjs';
import Exporter from './Exporter.mjs';
import Generator from './Generator.mjs';
import Parser from './Parser.mjs';
import Renderer from './Renderer.mjs';
import SequenceDiagram from './SequenceDiagram.mjs';
import {nodejs} from '../core/browser.mjs';
describe('SequenceDiagram', () => {
function getSimplifiedContent(d) {
@ -18,6 +19,7 @@ describe('SequenceDiagram', () => {
beforeEach(() => {
diagram = new SequenceDiagram({
document: new VirtualDocument(),
namespace: '',
textSizerFactory,
});
@ -167,6 +169,11 @@ describe('SequenceDiagram', () => {
});
it('measures OS fonts correctly on the first render', (done) => {
if(nodejs) {
pending('NodeJS font rendering not implemented yet');
return;
}
const code = 'title message';
const sd = new SequenceDiagram(code);
const widthImmediate = sd.getSize().width;
@ -187,6 +194,11 @@ describe('SequenceDiagram', () => {
});
it('measures embedded fonts correctly on the first render', (done) => {
if(nodejs) {
pending('NodeJS font rendering not implemented yet');
return;
}
const code = 'theme sketch\ntitle message';
const sd = new SequenceDiagram(code);
const widthImmediate = sd.getSize().width;

View File

@ -1,12 +1,12 @@
import ImageRegion from '../image/ImageRegion.js';
import SequenceDiagram from './SequenceDiagram.js';
import TESTS from './test-images/list.js';
import {matchers} from '../image/ImageSimilarity.js';
import ImageRegion from '../image/ImageRegion.mjs';
import SequenceDiagram from './SequenceDiagram.mjs';
import TESTS from '../../spec/images/list.mjs';
import {matchers} from '../image/ImageSimilarity.mjs';
describe('SequenceDiagram Visuals', () => {
const RESOLUTION = 4;
const IMAGE_BASE_PATH = 'scripts/sequence/test-images/';
const IMAGE_BASE_PATH = 'spec/images/';
const COLLAPSE_REGEX = new RegExp(/# collapse/g);

View File

@ -1,4 +1,4 @@
import CMMode from './CodeMirrorMode.js';
import CMMode from './CodeMirrorMode.mjs';
function execAt(str, reg, i) {
reg.lastIndex = i;

View File

@ -1,4 +1,4 @@
import Tokeniser from './Tokeniser.js';
import Tokeniser from './Tokeniser.mjs';
describe('Sequence Tokeniser', () => {
const tokeniser = new Tokeniser();

View File

@ -1,5 +1,5 @@
import BaseComponent, {register} from './BaseComponent.js';
import {mergeSets, removeAll} from '../../core/ArrayUtilities.js';
import BaseComponent, {register} from './BaseComponent.mjs';
import {mergeSets, removeAll} from '../../core/ArrayUtilities.mjs';
const OUTLINE_ATTRS = {
'class': 'outline',

View File

@ -1,5 +1,5 @@
import AgentCap from './AgentCap.js';
import {getComponents} from './BaseComponent.js';
import AgentCap from './AgentCap.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('AgentCap', () => {
it('registers itself with the component store', () => {

View File

@ -1,4 +1,4 @@
import BaseComponent, {register} from './BaseComponent.js';
import BaseComponent, {register} from './BaseComponent.mjs';
export default class AgentHighlight extends BaseComponent {
radius(highlighted, env) {

View File

@ -1,5 +1,5 @@
import AgentHighlight from './AgentHighlight.js';
import {getComponents} from './BaseComponent.js';
import AgentHighlight from './AgentHighlight.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('AgentHighlight', () => {
const highlight = new AgentHighlight();

View File

@ -1,5 +1,5 @@
import BaseComponent, {register} from './BaseComponent.js';
import {mergeSets, removeAll} from '../../core/ArrayUtilities.js';
import BaseComponent, {register} from './BaseComponent.mjs';
import {mergeSets, removeAll} from '../../core/ArrayUtilities.mjs';
const OUTLINE_ATTRS = {
'class': 'outline',

View File

@ -1,5 +1,5 @@
import {BlockBegin, BlockEnd, BlockSplit} from './Block.js';
import {getComponents} from './BaseComponent.js';
import {BlockBegin, BlockEnd, BlockSplit} from './Block.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('Block', () => {
it('registers itself with the component store', () => {

View File

@ -1,7 +1,7 @@
/* eslint-disable sort-keys */ // Maybe later
import BaseComponent, {register} from './BaseComponent.js';
import {mergeSets} from '../../core/ArrayUtilities.js';
import BaseComponent, {register} from './BaseComponent.mjs';
import {mergeSets} from '../../core/ArrayUtilities.mjs';
const OUTLINE_ATTRS = {
'class': 'outline',

View File

@ -1,5 +1,5 @@
import {Connect, ConnectDelayBegin, ConnectDelayEnd} from './Connect.js';
import {getComponents} from './BaseComponent.js';
import {Connect, ConnectDelayBegin, ConnectDelayEnd} from './Connect.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('Connect', () => {
it('registers itself with the component store', () => {

View File

@ -1,4 +1,4 @@
import BaseComponent, {register} from './BaseComponent.js';
import BaseComponent, {register} from './BaseComponent.mjs';
const OUTLINE_ATTRS = {
'class': 'outline',

View File

@ -1,5 +1,5 @@
import Divider from './Divider.js';
import {getComponents} from './BaseComponent.js';
import Divider from './Divider.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('Divider', () => {
describe('Divider', () => {

View File

@ -1,4 +1,4 @@
import BaseComponent, {register} from './BaseComponent.js';
import BaseComponent, {register} from './BaseComponent.mjs';
export class Mark extends BaseComponent {
makeState(state) {

View File

@ -1,5 +1,5 @@
import {Async, Mark} from './Marker.js';
import {getComponents} from './BaseComponent.js';
import {Async, Mark} from './Marker.mjs';
import {getComponents} from './BaseComponent.mjs';
const mark = new Mark();
const async = new Async();

View File

@ -1,7 +1,7 @@
/* eslint-disable complexity */ // Temporary ignore while switching linter
/* eslint-disable no-param-reassign */ // Also temporary
import BaseComponent, {register} from './BaseComponent.js';
import BaseComponent, {register} from './BaseComponent.mjs';
const OUTLINE_ATTRS = {
'class': 'outline',

View File

@ -1,5 +1,5 @@
import {NoteBetween, NoteOver, NoteSide} from './Note.js';
import {getComponents} from './BaseComponent.js';
import {NoteBetween, NoteOver, NoteSide} from './Note.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('NoteOver', () => {
it('registers itself with the component store', () => {

View File

@ -1,8 +1,8 @@
import BaseComponent, {
cleanRenderPreResult,
register,
} from './BaseComponent.js';
import {mergeSets} from '../../core/ArrayUtilities.js';
} from './BaseComponent.mjs';
import {mergeSets} from '../../core/ArrayUtilities.mjs';
function nullableMax(a = null, b = null) {
if(a === null) {

View File

@ -1,5 +1,5 @@
import Parallel from './Parallel.js';
import {getComponents} from './BaseComponent.js';
import Parallel from './Parallel.mjs';
import {getComponents} from './BaseComponent.mjs';
describe('Parallel', () => {
it('registers itself with the component store', () => {

View File

@ -1,6 +1,6 @@
/* eslint-disable sort-keys */ // Maybe later
import BaseTheme, {WavePattern} from './BaseTheme.js';
import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;

View File

@ -1,6 +1,6 @@
import {dom, textSizerFactory} from '../../stubs/TestDOM.js';
import {Factory} from './Basic.js';
import SVG from '../../svg/SVG.js';
import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
import {Factory} from './Basic.mjs';
import SVG from '../../svg/SVG.mjs';
describe('Basic Theme', () => {
const svg = new SVG(dom, textSizerFactory);

View File

@ -1,6 +1,6 @@
/* eslint-disable sort-keys */ // Maybe later
import BaseTheme, {WavePattern} from './BaseTheme.js';
import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'sans-serif';
const LINE_HEIGHT = 1.3;

View File

@ -1,6 +1,6 @@
import {dom, textSizerFactory} from '../../stubs/TestDOM.js';
import {Factory} from './Chunky.js';
import SVG from '../../svg/SVG.js';
import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
import {Factory} from './Chunky.mjs';
import SVG from '../../svg/SVG.mjs';
describe('Chunky Theme', () => {
const svg = new SVG(dom, textSizerFactory);

View File

@ -1,6 +1,6 @@
/* eslint-disable sort-keys */ // Maybe later
import BaseTheme, {WavePattern} from './BaseTheme.js';
import BaseTheme, {WavePattern} from './BaseTheme.mjs';
const FONT = 'monospace';
const LINE_HEIGHT = 1.3;

View File

@ -1,6 +1,6 @@
import {dom, textSizerFactory} from '../../stubs/TestDOM.js';
import {Factory} from './Monospace.js';
import SVG from '../../svg/SVG.js';
import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
import {Factory} from './Monospace.mjs';
import SVG from '../../svg/SVG.mjs';
describe('Monospace Theme', () => {
const svg = new SVG(dom, textSizerFactory);

View File

@ -1,9 +1,9 @@
/* eslint-disable max-lines */
/* eslint-disable sort-keys */ // Maybe later
import BaseTheme from './BaseTheme.js';
import Handlee from './HandleeFontData.js';
import Random from '../../core/Random.js';
import BaseTheme from './BaseTheme.mjs';
import Handlee from './HandleeFontData.mjs';
import Random from '../../core/Random.mjs';
const FONT = Handlee.name;
const FONT_FAMILY = '\'' + FONT + '\',cursive';

View File

@ -1,6 +1,6 @@
import {dom, textSizerFactory} from '../../stubs/TestDOM.js';
import {Factory} from './Sketch.js';
import SVG from '../../svg/SVG.js';
import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
import {Factory} from './Sketch.mjs';
import SVG from '../../svg/SVG.mjs';
describe('Sketch Theme', () => {
const svg = new SVG(dom, textSizerFactory);

View File

@ -1,37 +0,0 @@
import './core/ArrayUtilities_spec.js';
import './core/EventObject_spec.js';
import './core/Random_spec.js';
import './core/documents/VirtualDocument_spec.js';
import './svg/SVG_spec.js';
import './svg/SVGTextBlock_spec.js';
import './svg/PatternedLine_spec.js';
import './interface/Interface_spec.js';
import './interface/ComponentsLibrary_spec.js';
import './image/ImageRegion_spec.js';
import './image/Blur_spec.js';
import './image/Composition_spec.js';
import './image/ImageSimilarity_spec.js';
import './sequence/SequenceDiagram_spec.js';
import './sequence/SequenceDiagram_visual_spec.js';
import './sequence/Readme_spec.js';
import './sequence/Tokeniser_spec.js';
import './sequence/Parser_spec.js';
import './sequence/MarkdownParser_spec.js';
import './sequence/LabelPatternParser_spec.js';
import './sequence/Generator_spec.js';
import './sequence/Renderer_spec.js';
import './sequence/CodeMirrorMode_spec.js';
import './sequence/themes/Basic_spec.js';
import './sequence/themes/Monospace_spec.js';
import './sequence/themes/Chunky_spec.js';
import './sequence/themes/Sketch_spec.js';
import './sequence/components/AgentCap_spec.js';
import './sequence/components/AgentHighlight_spec.js';
import './sequence/components/Block_spec.js';
import './sequence/components/Connect_spec.js';
import './sequence/components/Divider_spec.js';
import './sequence/components/Marker_spec.js';
import './sequence/components/Note_spec.js';
import './sequence/components/Parallel_spec.js';
jasmine.executeAllTests();

View File

@ -1,4 +1,4 @@
import SequenceDiagram from './sequence/SequenceDiagram.js';
import SequenceDiagram from './sequence/SequenceDiagram.mjs';
const def = window.define;
if(def && def.amd) {

View File

@ -1,4 +1,4 @@
import PatternedLine from './PatternedLine.js';
import PatternedLine from './PatternedLine.mjs';
describe('PatternedLine', () => {
function simplify(path, dp) {

View File

@ -1,5 +1,5 @@
import {SVGTextBlock, TextSizer} from './SVGTextBlock.js';
import PatternedLine from './PatternedLine.js';
import {SVGTextBlock, TextSizer} from './SVGTextBlock.mjs';
import PatternedLine from './PatternedLine.mjs';
const NS = 'http://www.w3.org/2000/svg';
@ -214,16 +214,16 @@ export default class SVG {
return this.dom.el(tag, namespace);
}
box(attrs, position) {
return this.el('rect').attrs(attrs).attrs(position);
box(attrs, {height, width, x, y}) {
return this.el('rect').attrs(attrs).attrs({height, width, x, y});
}
boxFactory(attrs) {
return this.box.bind(this, attrs);
}
line(attrs, position) {
return this.el('line').attrs(attrs).attrs(position);
line(attrs, {x1, x2, y1, y2}) {
return this.el('line').attrs(attrs).attrs({x1, x2, y1, y2});
}
lineFactory(attrs) {
@ -259,11 +259,11 @@ export default class SVG {
return this.cross.bind(this, attrs);
}
note(attrs, flickAttrs, position) {
const x0 = position.x;
const x1 = position.x + position.width;
const y0 = position.y;
const y1 = position.y + position.height;
note(attrs, flickAttrs, {height, width, x, y}) {
const x0 = x;
const x1 = x + width;
const y0 = y;
const y1 = y + height;
const flick = 7;
return this.el('g').add(
@ -290,13 +290,13 @@ export default class SVG {
return this.note.bind(this, attrs, flickAttrs);
}
formattedText(attrs = {}, formatted = [], position = {}) {
formattedText(attrs = {}, formatted = [], {x, y} = {}) {
const container = this.el('g');
const txt = new SVGTextBlock(container, this, {
attrs,
formatted,
x: position.x,
y: position.y,
x,
y,
});
return Object.assign(container, {
set: (state) => txt.set(state),

View File

@ -1,5 +1,4 @@
// Thanks, https://stackoverflow.com/a/9851769/1180785
const firefox = (typeof window.InstallTrigger !== 'undefined');
import {firefox} from '../core/browser.mjs';
function merge(state, newState) {
for(const k in state) {

View File

@ -1,6 +1,7 @@
import {DOMWrapper, dom, textSizerFactory} from '../stubs/TestDOM.js';
import {SVGTextBlock, TextSizer} from './SVGTextBlock.js';
import SVG from './SVG.js';
import {DOMWrapper, dom, textSizerFactory} from '../../spec/stubs/TestDOM.mjs';
import {SVGTextBlock, TextSizer} from './SVGTextBlock.mjs';
import SVG from './SVG.mjs';
import {nodejs} from '../core/browser.mjs';
describe('SVGTextBlock', () => {
const attrs = {'font-size': 10, 'line-height': 1.5};
@ -128,6 +129,11 @@ describe('SVGTextBlock', () => {
});
describe('TextSizer', () => {
if(nodejs) {
// TextSizer is for browsers only
return;
}
beforeEach(() => {
svg = new SVG(
new DOMWrapper(window.document),

View File

@ -1,5 +1,5 @@
import {dom, textSizerFactory} from '../stubs/TestDOM.js';
import SVG from './SVG.js';
import {dom, textSizerFactory} from '../../spec/stubs/TestDOM.mjs';
import SVG from './SVG.mjs';
describe('SVG', () => {
const expectedNS = 'http://www.w3.org/2000/svg';

23
spec/helpers/toBeNear.mjs Normal file
View File

@ -0,0 +1,23 @@
beforeAll(() => {
jasmine.addMatchers({
toBeNear: () => ({
compare: (actual, expected, range) => {
if(
typeof expected !== 'number' ||
typeof range !== 'number' ||
range < 0
) {
throw new Error(
'Invalid toBeNear(' + expected + ',' + range + ')'
);
}
if(typeof actual !== 'number') {
throw new Error('Expected a number, got ' + actual);
}
return {
pass: Math.abs(actual - expected) <= range,
};
},
}),
});
});

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Before

Width:  |  Height:  |  Size: 992 B

After

Width:  |  Height:  |  Size: 992 B

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

6
spec/images/Sketch.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 27 KiB

Some files were not shown because too many files have changed in this diff Show More