Skip to content

Commit 2246ab9

Browse files
Ryan Dewrjrudin
authored andcommitted
GH #150 Support .mjs files in unit tests
1 parent f832c48 commit 2246ab9

File tree

10 files changed

+148
-101
lines changed

10 files changed

+148
-101
lines changed

marklogic-unit-test-client/src/test/ml-modules/root/test/suites/Helpers/Data Loaders/Load Test File/suite-teardown.xqy

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,18 @@ xquery version "1.0-ml";
22

33
import module namespace td="load-test-file-module" at "test-data/td.xqy";
44

5-
(
6-
xdmp:document-delete($td:URI-BASIC),
7-
xdmp:document-delete($td:URI-WITH-PERMS),
8-
xdmp:document-delete($td:URI-WITH-COLLECTIONS)
9-
)
5+
declare variable $deleteOptions as map:map := map:entry("ifNotExists", "allow");
6+
7+
let $safe-delete :=
8+
if (fn:function-available("xdmp:document-delete", 2)) then
9+
xdmp:document-delete(?, $deleteOptions)
10+
else
11+
function ($uri) {
12+
if (fn:doc-available($uri)) then xdmp:document-delete($uri) else ()
13+
}
14+
return
15+
(
16+
$safe-delete($td:URI-BASIC),
17+
$safe-delete($td:URI-WITH-PERMS),
18+
$safe-delete($td:URI-WITH-COLLECTIONS)
19+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declareUpdate();
2+
3+
xdmp.documentInsert("/test/mjs/setup.json", {});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declareUpdate();
2+
3+
xdmp.documentInsert("/test/mjs/suiteSetup.json", {});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declareUpdate();
2+
3+
xdmp.documentDelete("/test/mjs/suiteSetup.json");
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declareUpdate();
2+
3+
xdmp.documentDelete("/test/mjs/setup.json");
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { testHelperProxy } from "/test/test-helper";
2+
3+
const list = fn.head(xdmp.apply(xdmp.function(fn.QName("http://marklogic.com/test", "list"), "/test/test-controller.xqy")));
4+
5+
const assertions = [];
6+
assertions.push(testHelperProxy.assertExists(list.xpath(`test:suite[@path="JavaScript Modules"]
7+
/test:tests/test:test[@path="test.mjs"]`)));
8+
assertions.push(testHelperProxy.assertExists(cts.doc("/test/mjs/suiteSetup.json")));
9+
assertions.push(testHelperProxy.assertExists(cts.doc("/test/mjs/setup.json")));
10+
assertions;

marklogic-unit-test-modules/src/main/ml-modules/root/test/default.xqy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ declare function local:main() {
115115
</thead>
116116
<tbody>
117117
{
118-
for $suite at $index in test:list()/test:suite
118+
for $suite at $index in test:list(fn:true())/test:suite
119119
let $class := if ($index mod 2 = 1) then "odd" else "even"
120120
return
121121
(
@@ -139,11 +139,11 @@ declare function local:main() {
139139
<div class="wrapper"><input class="check-all-tests" type="checkbox" checked="checked"/>Run All Tests</div>
140140
<ul class="tests">
141141
{
142-
for $test in (<test:test path="suite-setup.xqy"/>, $suite/test:tests/test:test, <test:test path="suite-teardown.xqy"/>)
142+
for $test in ($suite/test:tests/test:test)
143143
return
144-
<li class="tests {if (fn:ends-with($test/@path, 'setup.xqy')) then 'setup-module-hidden' else if (fn:ends-with($test/@path, 'teardown.xqy')) then 'teardown-module-hidden' else ()}">
144+
<li class="tests {if (fn:matches($test/@path, '(S|s)etup\.')) then 'setup-module-hidden' else if (fn:matches($test/@path, '(T|t)eardown\.')) then 'teardown-module-hidden' else ()}">
145145
{
146-
if ($test/@path = "suite-setup.xqy" or $test/@path = "suite-teardown.xqy" or $test/@path = "suiteSetup.sjs" or $test/@path = "suiteTeardown.sjs") then
146+
if (fn:matches($test/@path, "^suite((-s|S)etup|(-t|T)eardown\.)")) then
147147
<input type="hidden" value="{fn:data($test/@path)}"/>
148148
else
149149
<input class="test-cb" type="checkbox" checked="checked" value="{fn:data($test/@path)}"/>,

marklogic-unit-test-modules/src/main/ml-modules/root/test/js/tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,12 @@ function suiteSuccess(parent, xml) {
235235
}
236236
errors.push(error);
237237

238-
if ($(this).parent().attr('name') == 'suite-setup.xqy') {
238+
if (/suite(-s|S)etup\./.test($(this).parent().attr('name'))) {
239239
var setupModule = parent.next().find('li.tests.setup-module-hidden');
240240
setupModule.removeClass('setup-module-hidden');
241241
setupModule.addClass('setup-module-visible');
242242
}
243-
if ($(this).parent().attr('name') == 'suite-teardown.xqy') {
243+
if (/suite(-t|T)eardown\./.test($(this).parent().attr('name'))) {
244244
var teardownModule = parent.next().find('li.tests.teardown-module-hidden');
245245
teardownModule.removeClass('teardown-module-hidden');
246246
teardownModule.addClass('teardown-module-visible');

marklogic-unit-test-modules/src/main/ml-modules/root/test/test-controller.xqy

Lines changed: 81 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,29 @@ declare variable $root as xs:string := xdmp:modules-root();
2121
: Returns a list of the available tests. This list is magically computed based on the modules
2222
:)
2323
declare function list()
24+
{
25+
list(fn:false())
26+
};
27+
(:
28+
: Returns a list of the available tests. This list is magically computed based on the modules
29+
:)
30+
declare function list($include-suite-setup-teardown as xs:boolean)
2431
{
2532
let $directories-to-ignore := map:new((
2633
".svn", "CVS", ".DS_Store", "Thumbs.db", "thumbs.db", "test-data", "lib"
2734
) ! map:entry(., .))
2835

29-
let $files-to-ignore := map:new((
30-
"setup.xqy", "teardown.xqy", "setup.sjs", "teardown.sjs",
31-
"suite-setup.xqy", "suite-teardown.xqy", "suiteSetup.sjs", "suiteTeardown.sjs"
32-
) ! map:entry(., .))
33-
36+
let $files-to-ignore-check := function ($file-name) {
37+
is-setup-module($file-name)
38+
or
39+
is-teardown-module($file-name)
40+
or
41+
(
42+
fn:not($include-suite-setup-teardown)
43+
and
44+
(is-suite-setup-module($file-name) or is-suite-teardown-module($file-name))
45+
)
46+
}
3447
let $suites := map:map()
3548

3649
let $_ :=
@@ -47,16 +60,16 @@ declare function list()
4760
let $test-is-valid :=
4861
$test-name
4962
and fn:not(fn:contains($test-name, "(\\|/)"))
50-
and fn:empty(map:get($files-to-ignore, $test-name))
51-
and fn:matches($test-name, "(\.sjs|\.xqy)$")
63+
and fn:not($files-to-ignore-check($test-name))
64+
and xdmp:uri-content-type($test-name) = ("application/xquery","application/vnd.marklogic-xdmp","application/javascript","application/vnd.marklogic-javascript", "application/vnd.marklogic-js-module")
5265

5366
where $suite-is-valid and $test-is-valid
5467
return map:put($suites, $suite-path, (map:get($suites, $suite-path), $test-name))
5568

5669
let $main-formats as xs:string* := fn:distinct-values(
5770
for $uri in test:list-from-database($db-id, $root || "test/formats/")
5871
let $path := fn:replace($uri, fn:concat($root, "test/formats/"), "")
59-
where $path ne "" and fn:not(fn:contains($path, "/")) and fn:empty(map:get($files-to-ignore, $path)) and (fn:matches($path, $XSL-PATTERN))
72+
where $path ne "" and fn:not(fn:contains($path, "/")) and fn:not($files-to-ignore-check($path)) and (fn:matches($path, $XSL-PATTERN))
6073
return $path
6174
)
6275
return
@@ -138,28 +151,28 @@ declare function run-suite(
138151
let $coverage :=
139152
if ($calculate-coverage) then
140153
(
141-
run-setup-teardown(fn:true(), $suite),
154+
run-suite-setup-or-teardown(fn:true(), $suite),
142155
let $coverage-modules := cover:list-coverage-modules()[fn:not(fn:starts-with(., $TEST-SUITES-ROOT))]
143156
let $test-modules := $tests ! fn:concat($TEST-SUITES-ROOT, $suite, "/", .)
144157
return cover:prepare($coverage-modules, $test-modules),
145158
if ($run-suite-teardown eq fn:true()) then
146-
run-setup-teardown(fn:false(), $suite)
159+
run-suite-setup-or-teardown(fn:false(), $suite)
147160
else ()
148161
)
149162
else ()
150163
let $results :=
151164
element test:run {
152165
test:log(" "),
153166
test:log(text {"SUITE:", $suite}),
154-
run-setup-teardown(fn:true(), $suite),
167+
run-suite-setup-or-teardown(fn:true(), $suite),
155168

156169
test:log(" - invoking tests"),
157170
for $test in $tests
158171
return
159172
run($suite, $test, fn:concat($TEST-SUITES-ROOT, $suite, "/", $test), $run-teardown, $coverage),
160173

161174
if ($run-suite-teardown eq fn:true()) then
162-
run-setup-teardown(fn:false(), $suite)
175+
run-suite-setup-or-teardown(fn:false(), $suite)
163176
else test:log(" - not running suite teardown"),
164177
test:log(" ")
165178
}
@@ -289,92 +302,70 @@ declare private function format-result($result as element(), $tab as xs:string?)
289302
)
290303
};
291304

292-
declare private function run-setup-or-teardown($setup as xs:boolean, $suite as xs:string)
305+
declare private function run-setup-or-teardown($is-setup as xs:boolean, $suite as xs:string)
293306
{
294-
let $stage := if ($setup) then "setup" else "teardown"
295-
let $xquery-script := $stage || ".xqy"
296-
let $sjs-script := $stage || ".sjs"
307+
let $start-time := xdmp:elapsed-time()
308+
let $suite-modules := test:list-from-database($db-id, $TEST-SUITES-ROOT || $suite || "/")
309+
let $stage := if ($is-setup) then "setup" else "teardown"
310+
let $module := fn:head(if ($is-setup) then $suite-modules[is-setup-module(.)] else $suite-modules[is-teardown-module(.)])
311+
where fn:exists($module)
297312
return
298-
try {
299-
(: We don't want the return value, so return () :)
300-
let $_ := test:log(" ...invoking " || $stage)
301-
let $_ := xdmp:invoke($TEST-SUITES-ROOT || $suite || "/" || $xquery-script)
302-
return ()
303-
}
304-
catch($ex) {
305-
if (($ex/error:code = "XDMP-MODNOTFOUND" and
306-
fn:matches($ex/error:stack/error:frame[1]/error:uri/fn:string(), "/" || $xquery-script || "$")) or
307-
($ex/error:code = "SVC-FILOPN" and
308-
fn:matches($ex/error:expr, $xquery-script))) then
309-
try {
310-
xdmp:invoke($TEST-SUITES-ROOT || $suite || "/" || $sjs-script)
311-
}
312-
catch($ex) {
313-
if (($ex/error:code = "XDMP-MODNOTFOUND" and
314-
fn:matches($ex/error:stack/error:frame[1]/error:uri/fn:string(), "/" || $sjs-script || "$")) or
315-
($ex/error:code = "SVC-FILOPN" and
316-
fn:matches($ex/error:expr, $sjs-script))) then
317-
()
318-
else
319-
element test:result {
320-
attribute type {"fail"},
321-
$ex
322-
}
323-
}
324-
else
325-
element test:result {
326-
attribute type {"fail"},
327-
$ex
328-
}
329-
}
313+
(: We don't want the return value, so only return if element(test:test) for failures :)
314+
let $result := invoke-setup-teardown-module($start-time, $stage, $module)
315+
where $result instance of element(test:test)
316+
return $result
330317
};
331318

332-
declare private function run-setup-teardown(
319+
declare private function run-suite-setup-or-teardown(
333320
$is-setup as xs:boolean,
334321
$suite as xs:string
335322
)
336323
{
337324
let $start-time := xdmp:elapsed-time()
338-
let $stage := if ($is-setup) then "setup" else "teardown"
339-
let $xquery-script := "suite-" || $stage || ".xqy"
340-
let $sjs-script := "suite" || xdmp:initcap($stage) || ".sjs"
325+
let $suite-modules := test:list-from-database($db-id, $TEST-SUITES-ROOT || $suite || "/")
326+
let $stage := "suite " || (if ($is-setup) then "setup" else "teardown")
327+
let $module := fn:head(if ($is-setup) then $suite-modules[is-suite-setup-module(.)] else $suite-modules[is-suite-teardown-module(.)])
328+
where fn:exists($module)
341329
return
342-
try {
343-
test:log(" - invoking suite " || $stage),
344-
xdmp:invoke($TEST-SUITES-ROOT || $suite || "/" || $xquery-script)
345-
}
346-
catch($ex) {
347-
if (($ex/error:code = "XDMP-MODNOTFOUND" and
348-
fn:matches($ex/error:stack/error:frame[1]/error:uri/fn:string(), "/" || $xquery-script || "$")) or
349-
($ex/error:code = "SVC-FILOPN" and
350-
fn:matches($ex/error:expr, $xquery-script))) then
351-
try {
352-
xdmp:invoke($TEST-SUITES-ROOT || $suite || "/" || $sjs-script)
353-
}
354-
catch ($ex) {
355-
if (($ex/error:code = "XDMP-MODNOTFOUND" and
356-
fn:matches($ex/error:stack/error:frame[1]/error:uri/fn:string(), "/" || $sjs-script || "$")) or
357-
($ex/error:code = "SVC-FILOPN" and
358-
fn:matches($ex/error:expr, $sjs-script))) then
359-
()
360-
else
361-
element test:test {
362-
attribute name { $sjs-script },
363-
attribute time { functx:total-seconds-from-duration(xdmp:elapsed-time() - $start-time) },
364-
element test:result {
365-
attribute type {"fail"},
366-
$ex
367-
}
368-
}
369-
}
370-
else
371-
element test:test {
372-
attribute name { $xquery-script },
373-
attribute time { functx:total-seconds-from-duration(xdmp:elapsed-time() - $start-time) },
374-
element test:result {
375-
attribute type {"fail"},
376-
$ex
377-
}
378-
}
379-
}
330+
invoke-setup-teardown-module($start-time, $stage, $module)
331+
};
332+
333+
declare function invoke-setup-teardown-module($start-time as xs:dayTimeDuration, $stage as xs:string, $module as xs:string) {
334+
try {
335+
test:log(" - invoking " || $stage),
336+
xdmp:invoke($module)
337+
}
338+
catch($ex) {
339+
if (($ex/error:code = "XDMP-MODNOTFOUND" and
340+
$ex/error:stack/error:frame[1]/error:uri/fn:string() = $module) or
341+
($ex/error:code = "SVC-FILOPN" and
342+
fn:contains($ex/error:expr, $module))) then
343+
()
344+
else
345+
element test:test {
346+
attribute name { $module },
347+
attribute time { functx:total-seconds-from-duration(xdmp:elapsed-time() - $start-time) },
348+
element test:result {
349+
attribute type {"fail"},
350+
$ex
351+
}
352+
}
353+
}
354+
};
355+
356+
(: setup/teardown checks :)
357+
declare function is-setup-module($file-name as xs:string) {
358+
fn:matches($file-name, "(^|/)setup\.")
359+
};
360+
361+
declare function is-teardown-module($file-name as xs:string) {
362+
fn:matches($file-name, "(^|/)teardown\.")
363+
};
364+
365+
declare function is-suite-setup-module($file-name as xs:string) {
366+
fn:matches($file-name, "(^|/)suite(-s|S)etup\.")
367+
};
368+
369+
declare function is-suite-teardown-module($file-name as xs:string) {
370+
fn:matches($file-name, "(^|/)suite(-t|T)eardown\.")
380371
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
const helperExportFunctions = {};
3+
4+
/*
5+
* XQuery code cannot be directly imported into JavaScript modules. This proxy provides a way to access marklogic-unit-test helper
6+
* functions in a JavaScript friendly (e.g., assertExists instead of assert-exists) without duplicating the test-helper code.
7+
*/
8+
const testHelperProxy = new Proxy(helperExportFunctions, {
9+
get: (target, prop) => {
10+
if (!target[prop]) {
11+
const xqueryFunctionName = prop.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
12+
target[prop] = xdmp.function(fn.QName("http://marklogic.com/test", xqueryFunctionName), "/test/test-helper.xqy");
13+
}
14+
const fun = target[prop];
15+
if (!(fun instanceof xdmp.function)) {
16+
throw new TypeError(`${prop} is not a test helper function.`);
17+
}
18+
return (...args) => xdmp.apply(fun, ...args);
19+
}
20+
});
21+
22+
export {
23+
testHelperProxy
24+
}

0 commit comments

Comments
 (0)