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 .DS_Store
xcuserdata xcuserdata
xcshareddata xcshareddata
ephemeral

View File

@ -7,18 +7,18 @@
<FileRef <FileRef
location = "container:scripts"> location = "container:scripts">
</FileRef> </FileRef>
<FileRef
location = "container:spec">
</FileRef>
<FileRef <FileRef
location = "container:styles"> location = "container:styles">
</FileRef> </FileRef>
<FileRef
location = "container:web">
</FileRef>
<FileRef <FileRef
location = "container:.gitignore"> location = "container:.gitignore">
</FileRef> </FileRef>
<FileRef
location = "container:.eslintrc.js">
</FileRef>
<FileRef
location = "group:rollup.config.js">
</FileRef>
<FileRef <FileRef
location = "container:editor-dev.htm"> location = "container:editor-dev.htm">
</FileRef> </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 The editor and library page do not require web modules, so should have
wider support. wider support.
To run the linter, run the command: To run the non-browser tests and linter, run the command:
```shell ```shell
npm run lint; npm test;
``` ```
And to rebuild the minified sources, run: And to rebuild the minified sources, run:
@ -52,7 +52,18 @@ And to rebuild the minified sources, run:
npm run minify; 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 ## Project Structure
@ -78,18 +89,19 @@ Useful helpers can also be found in `/scripts/core/*` and
`/scripts/svg/*`. `/scripts/svg/*`.
The live editor (index.htm & editor-dev.htm) uses the source in The live editor (index.htm & editor-dev.htm) uses the source in
`/scripts/editor.js` and `/scripts/interface/*`. Other pages use `/web/editor.mjs` and `/web/interface/*`. Other pages use sources in
sources in the root of `/scripts` as their entry-points. the root of `/web` as their entry-points.
## Testing ## Testing
The testing library used here is [Jasmine](https://jasmine.github.io/). The testing library used here is [Jasmine](https://jasmine.github.io/).
All test files follow the naming convention of `<filename>_spec.js`, All test files follow the naming convention of `<filename>_spec.mjs`
and must be listed in `/scripts/spec.js`. Linting automatically applies (commandline and browser) or `_webspec.mjs` (browser-only). Browser
to all files with a `.js` extension. 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: 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 * `Parser` and `Generator` stages have a good level of testing
* Rendering methods (SVG generation) have a minimal level of testing; * Rendering methods (SVG generation) have a minimal level of testing;
there are some high-level tests in there are some high-level tests in
`/scripts/sequence/SequenceDiagram_spec.js`, and a series of image `/scripts/sequence/SequenceDiagram_spec.mjs`, and a series of image
comparison tests in `/scripts/sequence/Readme_spec.js` (testing that comparison tests in `/scripts/sequence/Readme_spec.mjs` (testing that
the readme screenshots roughly match the current behaviour). Finally 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 comparison to test components and interactions using baseline SVGs
from `test-images`. from `spec/images`.
* The editor has a minimal level of testing. * The editor has a minimal level of testing.
If you suspect a failing test is not related to your changes, you can If you suspect a failing test is not related to your changes, you can
@ -129,7 +141,7 @@ polyfils are included.
### Testing & Linting ### Testing & Linting
Ensure that all unit tests are passing and files pass linting. This can 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 in a command window. At a minimum, you should ensure that tests are
passing in Google Chrome, but testing in other supported browsers is passing in Google Chrome, but testing in other supported browsers is
even better. even better.
@ -150,7 +162,7 @@ npm run minify;
``` ```
This will update the files in `/lib` and `/weblib`. The minified code 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 script, with some boiler-plate added to allow loading into a page in a
variety of ways. variety of ways.

View File

@ -3,7 +3,7 @@ reference it with #issue-number.
Checklist: (tick with [x]) 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. - [ ] Tests are passing in Google Chrome.
- [ ] Linting is passing (`npm run lint`) - [ ] Linting is passing (`npm run lint`)
- [ ] No dead code is left behind. - [ ] No dead code is left behind.

View File

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

View File

@ -1239,8 +1239,19 @@
} }
} }
const nodejs = (typeof window === 'undefined');
// Thanks, https://stackoverflow.com/a/23522755/1180785 // 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 { class Exporter {
constructor() { constructor() {
@ -6620,9 +6631,6 @@
} }
} }
// Thanks, https://stackoverflow.com/a/9851769/1180785
const firefox = (typeof window.InstallTrigger !== 'undefined');
function merge(state, newState) { function merge(state, newState) {
for(const k in state) { for(const k in state) {
if(Object.prototype.hasOwnProperty.call(state, k)) { if(Object.prototype.hasOwnProperty.call(state, k)) {
@ -7115,16 +7123,16 @@
return this.dom.el(tag, namespace); return this.dom.el(tag, namespace);
} }
box(attrs, position) { box(attrs, {height, width, x, y}) {
return this.el('rect').attrs(attrs).attrs(position); return this.el('rect').attrs(attrs).attrs({height, width, x, y});
} }
boxFactory(attrs) { boxFactory(attrs) {
return this.box.bind(this, attrs); return this.box.bind(this, attrs);
} }
line(attrs, position) { line(attrs, {x1, x2, y1, y2}) {
return this.el('line').attrs(attrs).attrs(position); return this.el('line').attrs(attrs).attrs({x1, x2, y1, y2});
} }
lineFactory(attrs) { lineFactory(attrs) {
@ -7160,11 +7168,11 @@
return this.cross.bind(this, attrs); return this.cross.bind(this, attrs);
} }
note(attrs, flickAttrs, position) { note(attrs, flickAttrs, {height, width, x, y}) {
const x0 = position.x; const x0 = x;
const x1 = position.x + position.width; const x1 = x + width;
const y0 = position.y; const y0 = y;
const y1 = position.y + position.height; const y1 = y + height;
const flick = 7; const flick = 7;
return this.el('g').add( return this.el('g').add(
@ -7191,13 +7199,13 @@
return this.note.bind(this, attrs, flickAttrs); return this.note.bind(this, attrs, flickAttrs);
} }
formattedText(attrs = {}, formatted = [], position = {}) { formattedText(attrs = {}, formatted = [], {x, y} = {}) {
const container = this.el('g'); const container = this.el('g');
const txt = new SVGTextBlock(container, this, { const txt = new SVGTextBlock(container, this, {
attrs, attrs,
formatted, formatted,
x: position.x, x,
y: position.y, y,
}); });
return Object.assign(container, { return Object.assign(container, {
set: (state) => txt.set(state), set: (state) => txt.set(state),
@ -9544,7 +9552,9 @@
function pickDocument(container) { function pickDocument(container) {
if(container) { if(container) {
return container.ownerDocument; return container.ownerDocument || null;
} else if(typeof window === 'undefined') {
return null;
} else { } else {
return window.document; return window.document;
} }
@ -9921,6 +9931,10 @@
convert(els); convert(els);
} }
function getDefaultThemeNames() {
return themes.map((theme) => theme.name);
}
Object.assign(SequenceDiagram, { Object.assign(SequenceDiagram, {
Exporter, Exporter,
Generator, Generator,
@ -9930,6 +9944,7 @@
convert, convert,
convertAll, convertAll,
extractCodeFromSVG, extractCodeFromSVG,
getDefaultThemeNames,
registerCodeMirrorMode, registerCodeMirrorMode,
renderAll, renderAll,
themes, 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==", "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
"dev": true "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": { "array-union": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@ -144,6 +150,12 @@
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
"dev": true "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": { "babel-code-frame": {
"version": "6.26.0", "version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@ -580,6 +592,24 @@
"fill-range": "2.2.3" "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": { "external-editor": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
@ -683,6 +713,12 @@
"for-in": "1.0.2" "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": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -728,6 +764,28 @@
"is-glob": "2.0.1" "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": { "globals": {
"version": "11.4.0", "version": "11.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-11.4.0.tgz",
@ -769,12 +827,30 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true "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": { "he": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true "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": { "http-proxy": {
"version": "1.16.2", "version": "1.16.2",
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz",
@ -838,6 +914,12 @@
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
"dev": true "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": { "inquirer": {
"version": "3.3.0", "version": "3.3.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz",
@ -980,6 +1062,18 @@
"integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
"dev": true "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": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -1001,6 +1095,22 @@
"isarray": "1.0.0" "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": { "js-tokens": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
@ -1038,6 +1148,15 @@
"is-buffer": "1.1.6" "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": { "levn": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@ -1070,6 +1189,23 @@
"yallist": "2.1.2" "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": { "micromatch": {
"version": "2.3.11", "version": "2.3.11",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", "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": { "os-tmpdir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
@ -1266,6 +1408,12 @@
"integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=", "integrity": "sha1-VjRtR0nXjyNDDKDHE4UK75GqNh0=",
"dev": true "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": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@ -1488,6 +1636,16 @@
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
"dev": true "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": { "resolve-from": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
@ -1538,6 +1696,15 @@
"integrity": "sha512-MlxPQTkMtiRUtyhIJ7FpBvTzWtar8eFBA+V7/J6Deg9fSgIIHwL6bJKK1Wl1uWSWtOrWhOmtsMwb9F6aagP/Pg==", "integrity": "sha512-MlxPQTkMtiRUtyhIJ7FpBvTzWtar8eFBA+V7/J6Deg9fSgIIHwL6bJKK1Wl1uWSWtOrWhOmtsMwb9F6aagP/Pg==",
"dev": true "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": { "rollup-pluginutils": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz", "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz",
@ -1590,6 +1757,15 @@
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true "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": { "shebang-command": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@ -1626,6 +1802,15 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true "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": { "sourcemap-codec": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz",
@ -1727,6 +1912,15 @@
"os-tmpdir": "1.0.2" "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": { "type-check": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",

View File

@ -15,19 +15,26 @@
"lib/sequence-diagram.js" "lib/sequence-diagram.js"
], ],
"main": "lib/sequence-diagram", "main": "lib/sequence-diagram",
"module": "source/standalone", "module": "scripts/standalone",
"scripts": { "scripts": {
"lint": "eslint scripts --ext .js --ignore-pattern '!.eslintrc.js' --ignore-pattern '*FontData.js'", "lint": "eslint . --config spec/support/eslintrc.js --ignore-path spec/support/eslintignore --ext .js --ext .mjs",
"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", "minify-lib": "rollup --config scripts/rollup.config.js && uglifyjs --compress --mangle --warn --output lib/sequence-diagram.min.js -- lib/sequence-diagram.js",
"start": "http-server" "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": { "devDependencies": {
"eslint": "^4.19.1", "eslint": "^4.19.1",
"eslint-plugin-jasmine": "^2.9.3", "eslint-plugin-jasmine": "^2.9.3",
"http-server": "^0.10.0", "http-server": "^0.10.0",
"jasmine": "^3.1.0",
"requirejs": "2.3.5", "requirejs": "2.3.5",
"rollup": "^0.57.1", "rollup": "^0.57.1",
"rollup-plugin-hypothetical": "^2.1.0", "rollup-plugin-hypothetical": "^2.1.0",
"rollup-plugin-multi-entry": "^2.0.2",
"source-map-support": "^0.5.4",
"uglify-es": "^3.1.10" "uglify-es": "^3.1.10"
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import Random from './Random.js'; import Random from './Random.mjs';
describe('Random', () => { describe('Random', () => {
let random = null; 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; 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() { get firstChild() {
return this.childNodes[0] || null; return this.childNodes[0] || null;
} }
@ -195,7 +228,7 @@ class ElementNode {
} }
} }
export default class VirtualDocument { export class VirtualDocument {
createElement(tag) { createElement(tag) {
return new ElementNode(this, tag, ''); return new ElementNode(this, tag, '');
} }
@ -208,3 +241,9 @@ export default class VirtualDocument {
return new TextNode(content); 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', () => { describe('VirtualDocument', () => {
const doc = new 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) { export function makeGaussianKernel(size) {
const sz = Math.ceil(size * 3); const sz = Math.ceil(size * 3);

View File

@ -1,5 +1,5 @@
import ImageRegion from './ImageRegion.js'; import ImageRegion from './ImageRegion.mjs';
import {blur2D} from './Blur.js'; import {blur2D} from './Blur.mjs';
describe('Blur', () => { describe('Blur', () => {
const PRECISION = 0.01; 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} = {}) { function compose(input1, input2, fn, {target = null} = {}) {
input1.checkCompatible(input2); input1.checkCompatible(input2);

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import {isSimilar, matchers} from './ImageSimilarity.js'; import {isSimilar, matchers} from './ImageSimilarity.mjs';
import ImageRegion from './ImageRegion.js'; import ImageRegion from './ImageRegion.mjs';
describe('ImageSimilarity', () => { describe('ImageSimilarity', () => {
let inputA = null; 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 /* 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 TRIMMER = /^([ \t]*)(.*)$/;
const SQUASH = { const SQUASH = {

View File

@ -2,7 +2,7 @@
/* eslint-disable sort-keys */ // Maybe later /* eslint-disable sort-keys */ // Maybe later
/* eslint-disable complexity */ // Temporary ignore while switching linter /* 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}}; const CM_ERROR = {type: 'error line-error', suggest: false, then: {'': 0}};

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
/* eslint-disable max-statements */ /* eslint-disable max-statements */
/* eslint-disable sort-keys */ // Maybe later /* eslint-disable sort-keys */ // Maybe later
import Generator from './Generator.js'; import Generator from './Generator.mjs';
describe('Sequence Generator', () => { describe('Sequence Generator', () => {
const generator = new 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', () => { describe('Label Pattern Parser', () => {
it('converts simple text', () => { it('converts simple text', () => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,12 +1,12 @@
import ImageRegion from '../image/ImageRegion.js'; import ImageRegion from '../image/ImageRegion.mjs';
import SequenceDiagram from './SequenceDiagram.js'; import SequenceDiagram from './SequenceDiagram.mjs';
import TESTS from './test-images/list.js'; import TESTS from '../../spec/images/list.mjs';
import {matchers} from '../image/ImageSimilarity.js'; import {matchers} from '../image/ImageSimilarity.mjs';
describe('SequenceDiagram Visuals', () => { describe('SequenceDiagram Visuals', () => {
const RESOLUTION = 4; const RESOLUTION = 4;
const IMAGE_BASE_PATH = 'scripts/sequence/test-images/'; const IMAGE_BASE_PATH = 'spec/images/';
const COLLAPSE_REGEX = new RegExp(/# collapse/g); 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) { function execAt(str, reg, i) {
reg.lastIndex = i; reg.lastIndex = i;

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import AgentCap from './AgentCap.js'; import AgentCap from './AgentCap.mjs';
import {getComponents} from './BaseComponent.js'; import {getComponents} from './BaseComponent.mjs';
describe('AgentCap', () => { describe('AgentCap', () => {
it('registers itself with the component store', () => { 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 { export default class AgentHighlight extends BaseComponent {
radius(highlighted, env) { radius(highlighted, env) {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import {Connect, ConnectDelayBegin, ConnectDelayEnd} from './Connect.js'; import {Connect, ConnectDelayBegin, ConnectDelayEnd} from './Connect.mjs';
import {getComponents} from './BaseComponent.js'; import {getComponents} from './BaseComponent.mjs';
describe('Connect', () => { describe('Connect', () => {
it('registers itself with the component store', () => { 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 = { const OUTLINE_ATTRS = {
'class': 'outline', 'class': 'outline',

View File

@ -1,5 +1,5 @@
import Divider from './Divider.js'; import Divider from './Divider.mjs';
import {getComponents} from './BaseComponent.js'; import {getComponents} from './BaseComponent.mjs';
describe('Divider', () => { describe('Divider', () => {
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 { export class Mark extends BaseComponent {
makeState(state) { makeState(state) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import {dom, textSizerFactory} from '../../stubs/TestDOM.js'; import {dom, textSizerFactory} from '../../../spec/stubs/TestDOM.mjs';
import {Factory} from './Sketch.js'; import {Factory} from './Sketch.mjs';
import SVG from '../../svg/SVG.js'; import SVG from '../../svg/SVG.mjs';
describe('Sketch Theme', () => { describe('Sketch Theme', () => {
const svg = new SVG(dom, textSizerFactory); 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; const def = window.define;
if(def && def.amd) { if(def && def.amd) {

View File

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

View File

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

View File

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

View File

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

View File

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