Move tests to /xt as they are RELEASE tests
Squashed commit of the following: commit 606d572db561d8de659bce6ac96252f8a1d7ee29 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat May 6 14:56:07 2017 +0100 tests should be release tests, so move to xt/ commit7673f3ee1eAuthor: Oliver Gorwits <oliver@cpan.org> Date: Sat May 6 14:19:19 2017 +0100 allow check_acl to accept Device or NetAddr::IP instance commitc31059bc01Author: Oliver Gorwits <oliver@cpan.org> Date: Sat May 6 14:19:00 2017 +0100 update docs commitdeaeab2670Author: Oliver Gorwits <oliver@cpan.org> Date: Sat May 6 14:18:27 2017 +0100 SNMP only stanza has access to full check_acl features commit4a44fa5863Author: Oliver Gorwits <oliver@cpan.org> Date: Mon May 1 18:49:38 2017 +0100 add AND operator and negation support to ACLs
This commit is contained in:
28
xt/10-sort_port.t
Normal file
28
xt/10-sort_port.t
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict; use warnings FATAL => 'all';
|
||||
use Test::More 0.88;
|
||||
|
||||
BEGIN {
|
||||
use_ok( 'App::Netdisco::Util::Web', 'sort_port' );
|
||||
}
|
||||
|
||||
# 1 = first is greater
|
||||
# 0 = same
|
||||
# -1 = first is lesser
|
||||
|
||||
is(sort_port(1,1), 0, 'number - same values');
|
||||
is(sort_port('1:2','1:10'), -1, 'colon number (Extreme) - first lesser');
|
||||
is(sort_port('D1','D10'), -1, 'HP - simple letter and number [#152]');
|
||||
|
||||
# Juniper examples for [#128]
|
||||
# https://www.juniper.net/documentation/en_US/junos14.2/topics/concept/interfaces-interface-naming-overview.html
|
||||
is(sort_port('so-1/0/0.0','so-1/0/1.0'), -1, 'juniper - FPC in slot 1 with OC3 PIC - 1');
|
||||
is(sort_port('so-1/1/0.0','so-1/1/1.0'), -1, 'juniper - FPC in slot 1 with OC3 PIC - 2');
|
||||
is(sort_port('so-1/0/0.0','so-1/1/0.0'), -1, 'juniper - FPC in slot 1 with OC3 PIC - 3');
|
||||
|
||||
is(sort_port('so-1/0/0:0','so-1/0/1:0'), -1, 'juniper - FPC in slot 1 with OC3 PIC channelized - 1');
|
||||
is(sort_port('so-1/1/0:0','so-1/1/1:0'), -1, 'juniper - FPC in slot 1 with OC3 PIC channelized - 2');
|
||||
is(sort_port('so-1/0/0:0','so-1/1/0:0'), -1, 'juniper - FPC in slot 1 with OC3 PIC channelized - 3');
|
||||
|
||||
done_testing;
|
||||
18
xt/11-portsort.t
Normal file
18
xt/11-portsort.t
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Test::More;
|
||||
use Env::Path;
|
||||
use FindBin qw( $Bin );
|
||||
|
||||
my @phantomjs = Env::Path->PATH->Whence('phantomjs');
|
||||
my $phantomjs = scalar @phantomjs ? $phantomjs[0] : $ENV{ND_PHANTOMJS};
|
||||
|
||||
if ( ! defined $phantomjs or !-x $phantomjs ) {
|
||||
plan skip_all =>
|
||||
"phantomjs not found, please set ND_PHANTOMJS or install phantomjs to the default location";
|
||||
}
|
||||
else {
|
||||
exec( $phantomjs, "$Bin/js/run_qunit.js", "$Bin/html/portsort.html" );
|
||||
}
|
||||
94
xt/20-checkacl.t
Normal file
94
xt/20-checkacl.t
Normal file
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use strict; use warnings FATAL => 'all';
|
||||
use Test::More 1.302083;
|
||||
|
||||
BEGIN {
|
||||
use_ok( 'App::Netdisco::Util::Permission', 'check_acl' );
|
||||
}
|
||||
|
||||
my @conf = (
|
||||
# +ve match -ve match
|
||||
'localhost', '!www.example.com', # 0, 1
|
||||
'127.0.0.1', '!192.0.2.1', # 2, 3
|
||||
'::1', '!2001:db8::1', # 4, 5
|
||||
'127.0.0.0/29', '!192.0.2.0/24', # 6, 7
|
||||
'::1/128', '!2001:db8::/32', # 8, 9
|
||||
|
||||
'127.0.0.1-10', '!192.0.2.1-10', # 10,11
|
||||
'::1-10', '!2001:db8::1-10', # 12,13
|
||||
|
||||
qr/^localhost$/, qr/^www.example.com$/, # 14,15
|
||||
qr/(?!:www.example.com)/, '!127.0.0.0/29', # 16,17
|
||||
'!127.0.0.1-10', qr/(?!:localhost)/, # 18,19
|
||||
|
||||
'op:and', # 20
|
||||
);
|
||||
|
||||
# name, ipv4, ipv6, v4 prefix, v6 prefix
|
||||
ok(check_acl('localhost',[$conf[0]]), 'same name');
|
||||
ok(check_acl('127.0.0.1',[$conf[2]]), 'same ipv4');
|
||||
ok(check_acl('::1',[$conf[4]]), 'same ipv6');
|
||||
ok(check_acl('127.0.0.0/29',[$conf[6]]), 'same v4 prefix');
|
||||
ok(check_acl('::1/128',[$conf[8]]), 'same v6 prefix');
|
||||
|
||||
# failed name, ipv4, ipv6, v4 prefix, v6 prefix
|
||||
is(check_acl('www.microsoft.com',[$conf[0]]), 0, 'failed name');
|
||||
is(check_acl('172.20.0.1',[$conf[2]]), 0, 'failed ipv4');
|
||||
is(check_acl('2001:db8::5',[$conf[4]]), 0, 'failed ipv6');
|
||||
is(check_acl('172.16.1.3/29',[$conf[6]]), 0, 'failed v4 prefix');
|
||||
is(check_acl('2001:db8:f00d::/64',[$conf[8]]), 0, 'failed v6 prefix');
|
||||
|
||||
# negated name, ipv4, ipv6, v4 prefix, v6 prefix
|
||||
ok(check_acl('localhost',[$conf[1]]), 'not same name');
|
||||
ok(check_acl('127.0.0.1',[$conf[3]]), 'not same ipv4');
|
||||
ok(check_acl('::1',[$conf[5]]), 'not same ipv6');
|
||||
ok(check_acl('127.0.0.0/29',[$conf[7]]), 'not same v4 prefix');
|
||||
ok(check_acl('::1/128',[$conf[9]]), 'not same v6 prefix');
|
||||
|
||||
# v4 range, v6 range
|
||||
ok(check_acl('127.0.0.1',[$conf[10]]), 'in v4 range');
|
||||
ok(check_acl('::1',[$conf[12]]), 'in v6 range');
|
||||
|
||||
# failed v4 range, v6 range
|
||||
is(check_acl('172.20.0.1',[$conf[10]]), 0, 'failed v4 range');
|
||||
is(check_acl('2001:db8::5',[$conf[12]]), 0, 'failed v6 range');
|
||||
|
||||
# negated v4 range, v6 range
|
||||
ok(check_acl('127.0.0.1',[$conf[11]]), 'not in v4 range');
|
||||
ok(check_acl('::1',[$conf[13]]), 'not in v6 range');
|
||||
|
||||
# hostname regexp
|
||||
ok(check_acl('localhost',[$conf[14]]), 'name regexp');
|
||||
ok(check_acl('127.0.0.1',[$conf[14]]), 'IP regexp');
|
||||
is(check_acl('www.google.com',[$conf[14]]), 0, 'failed regexp');
|
||||
|
||||
# OR of prefix, range, regexp, property (2 of, 3 of, 4 of)
|
||||
ok(check_acl('127.0.0.1',[@conf[8,0]]), 'OR: prefix, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[8,12,0]]), 'OR: prefix, range, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[8,12,15,0]]), 'OR: prefix, range, regexp, name');
|
||||
|
||||
# OR of negated prefix, range, regexp, property (2 of, 3 of, 4 of)
|
||||
ok(check_acl('127.0.0.1',[@conf[17,0]]), 'OR: !prefix, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[17,18,0]]), 'OR: !prefix, !range, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[17,18,19,0]]), 'OR: !prefix, !range, !regexp, name');
|
||||
|
||||
# AND of prefix, range, regexp, property (2 of, 3 of, 4 of)
|
||||
ok(check_acl('127.0.0.1',[@conf[6,0,20]]), 'AND: prefix, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[6,10,0,20]]), 'AND: prefix, range, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[6,10,14,0,20]]), 'AND: prefix, range, regexp, name');
|
||||
|
||||
# failed AND on prefix, range, regexp
|
||||
is(check_acl('127.0.0.1',[@conf[8,10,14,0,20]]), 0, 'failed AND: prefix!, range, regexp, name');
|
||||
is(check_acl('127.0.0.1',[@conf[6,12,14,0,20]]), 0, 'failed AND: prefix, range!, regexp, name');
|
||||
is(check_acl('127.0.0.1',[@conf[6,10,15,0,20]]), 0, 'failed AND: prefix, range, regexp!, name');
|
||||
|
||||
# AND of negated prefix, range, regexp, property (2 of, 3 of, 4 of)
|
||||
ok(check_acl('127.0.0.1',[@conf[9,0,20]]), 'AND: !prefix, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[7,11,0,20]]), 'AND: !prefix, !range, name');
|
||||
ok(check_acl('127.0.0.1',[@conf[9,13,16,0,20]]), 'AND: !prefix, !range, !regexp, name');
|
||||
|
||||
# device property
|
||||
# negated device property
|
||||
|
||||
done_testing;
|
||||
255
xt/html/portsort.html
Normal file
255
xt/html/portsort.html
Normal file
@@ -0,0 +1,255 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>portsort.js test suite</title>
|
||||
<script src="http://code.jquery.com/jquery-1.11.1.min.js" type="text/javascript"></script>
|
||||
<script src="http://code.jquery.com/qunit/qunit-1.14.0.js" type="text/javascript"></script>
|
||||
<script src="../js/qunit-tap.js" type="text/javascript"></script>
|
||||
<script>
|
||||
qunitTap(QUnit, function() { console.log.apply(console, arguments); });
|
||||
</script>
|
||||
<link href="http://code.jquery.com/qunit/qunit-1.14.0.css" type="text/css" rel="stylesheet"></link>
|
||||
<script src="../../share/public/javascripts/jquery.dataTables.min.js" type="text/javascript"></script>
|
||||
<script src="../../share/public/javascripts/portsort.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
module("portsort.js"),
|
||||
test("different values types", function () {
|
||||
wrapTest(
|
||||
['a',1],
|
||||
[1,'a'],
|
||||
'number always comes first');
|
||||
wrapTest(
|
||||
['1',1],
|
||||
['1',1],
|
||||
'number vs numeric string - should remain unchanged (error in chrome)');
|
||||
wrapTest(
|
||||
['02',3,2,'01'],
|
||||
['01','02',2,3],
|
||||
'padding numeric string vs number');
|
||||
});
|
||||
test("numerics", function () {
|
||||
wrapTest(
|
||||
['10',9,2,'1','4'],
|
||||
['1',2,'4',9,'10'],
|
||||
'string vs number');
|
||||
wrapTest(
|
||||
['0001','002','001'],
|
||||
['0001','001','002'],
|
||||
'0 left-padded numbers');
|
||||
wrapTest(
|
||||
[2,1,'1','0001','002','02','001'],
|
||||
['0001','001','002','02',1,'1',2],
|
||||
'0 left-padded numbers and regular numbers');
|
||||
wrapTest(
|
||||
['10.0401',10.022,10.042,'10.021999'],
|
||||
['10.021999',10.022,'10.0401',10.042],
|
||||
'decimal string vs decimal, different precision');
|
||||
wrapTest(
|
||||
['10.04',10.02,10.03,'10.01'],
|
||||
['10.01',10.02,10.03,'10.04'],
|
||||
'decimal string vs decimal, same precision');
|
||||
wrapTest(
|
||||
['10001','10011','101','10010','10','100','10002',
|
||||
'10112','10111'],
|
||||
['10','100','101','10001','10002','10010','10011',
|
||||
'10111','10112'],
|
||||
'mixed length');
|
||||
});
|
||||
test("IP addresses", function () {
|
||||
wrapTest(
|
||||
[
|
||||
'192.168.0.100',
|
||||
'192.168.0.1',
|
||||
'192.168.1.1',
|
||||
'192.168.0.250',
|
||||
'192.168.1.123',
|
||||
'10.0.0.2',
|
||||
'10.0.0.1'
|
||||
],
|
||||
[
|
||||
'10.0.0.1',
|
||||
'10.0.0.2',
|
||||
'192.168.0.1',
|
||||
'192.168.0.100',
|
||||
'192.168.0.250',
|
||||
'192.168.1.1',
|
||||
'192.168.1.123'
|
||||
]);
|
||||
});
|
||||
test("space(s) as first character(s)", function () {
|
||||
wrapTest(
|
||||
['alpha',' 1',' 3',' 2',0],
|
||||
[0,' 1',' 2',' 3','alpha']);
|
||||
});
|
||||
test("wireless controllers", function () {
|
||||
wrapTest(
|
||||
[
|
||||
'00:14:0e:12:34:56','00:08:30:01:23:45.1',
|
||||
'00:15:c7:ab:23:10.0','00:14:0e:01:23:45',
|
||||
'00:08:30:01:23:45.0','00:15:c7:ab:23:00.1'
|
||||
],
|
||||
[
|
||||
'00:08:30:01:23:45.0','00:08:30:01:23:45.1',
|
||||
'00:14:0e:01:23:45','00:14:0e:12:34:56',
|
||||
'00:15:c7:ab:23:00.1','00:15:c7:ab:23:10.0'
|
||||
],'ap ports');
|
||||
wrapTest(
|
||||
[
|
||||
'wlan-controller1/0.104','wlan-controller1/0',
|
||||
'wlan-controller1/0.252','wlan-controller1/0.103'
|
||||
],
|
||||
[
|
||||
'wlan-controller1/0','wlan-controller1/0.103',
|
||||
'wlan-controller1/0.104','wlan-controller1/0.252'
|
||||
],'controller ports');
|
||||
});
|
||||
test("ports", function () {
|
||||
wrapTest(
|
||||
[
|
||||
'1.1','1.13','1.14','1.19','1.2','Vlan318','1.25',
|
||||
'1.29','3.12','1.3','1.37','1.38','1.4','1.43',
|
||||
'1.6','8.34'
|
||||
],
|
||||
[
|
||||
'1.1','1.2','1.3','1.4','1.6','1.13','1.14','1.19',
|
||||
'1.25','1.29','1.37','1.38','1.43','3.12','8.34',
|
||||
'Vlan318'
|
||||
],'Avaya');
|
||||
wrapTest(
|
||||
[
|
||||
'GigabitEthernet9/0/12',
|
||||
'GigabitEthernet9/0/11',
|
||||
'GigabitEthernet1/0/14',
|
||||
'GigabitEthernet1/1/12',
|
||||
],
|
||||
[
|
||||
'GigabitEthernet1/0/14',
|
||||
'GigabitEthernet1/1/12',
|
||||
'GigabitEthernet9/0/11',
|
||||
'GigabitEthernet9/0/12',
|
||||
],'Cisco');
|
||||
wrapTest(
|
||||
[
|
||||
'1/1/1','0/1/1','0/3/20','0/2/1','0/3/1','0/3/2',
|
||||
'0/3/11','0/3/10'
|
||||
|
||||
],[
|
||||
'0/1/1','0/2/1','0/3/1','0/3/2','0/3/10','0/3/11',
|
||||
'0/3/20','1/1/1'
|
||||
],'Dell');
|
||||
wrapTest(
|
||||
[
|
||||
'1:10','1:2','1:1','1:11'
|
||||
|
||||
],[
|
||||
'1:1','1:2','1:10','1:11'
|
||||
],'Extreme');
|
||||
wrapTest(
|
||||
[
|
||||
'D10','D11','D2','D1', 'A30', 'A3'
|
||||
],[
|
||||
'A3', 'A30', 'D1','D2','D10','D11'
|
||||
],'HP A & D');
|
||||
wrapTest(
|
||||
[
|
||||
'B10','B11','B2','B1', 'A30', 'A3'
|
||||
],[
|
||||
'A3', 'A30', 'B1','B2','B10','B11'
|
||||
],'HP A & B');
|
||||
wrapTest(
|
||||
[
|
||||
'10GigabitEthernet1/1/12',
|
||||
'GigabitEthernet1/0/14',
|
||||
'GigabitEthernet9/0/12',
|
||||
'10GigabitEthernet9/0/11',
|
||||
],[
|
||||
'GigabitEthernet1/0/14',
|
||||
'10GigabitEthernet1/1/12',
|
||||
'10GigabitEthernet9/0/11',
|
||||
'GigabitEthernet9/0/12',
|
||||
],'Foundry 10Gb');
|
||||
wrapTest(
|
||||
[
|
||||
'Slot: 1 Port: 2 Gigabit - Level',
|
||||
'Slot: 1 Port: 1 Gigabit - Level',
|
||||
'Slot: 0 Port: 15 Gigabit - Level',
|
||||
'Slot: 1 Port: 10 Gigabit - Level',
|
||||
'Slot: 0 Port: 1 Gigabit - Level'
|
||||
],[
|
||||
'Slot: 0 Port: 1 Gigabit - Level',
|
||||
'Slot: 0 Port: 15 Gigabit - Level',
|
||||
'Slot: 1 Port: 1 Gigabit - Level',
|
||||
'Slot: 1 Port: 2 Gigabit - Level',
|
||||
'Slot: 1 Port: 10 Gigabit - Level'
|
||||
],'Netgear');
|
||||
wrapTest(
|
||||
[
|
||||
'port-channel190','port-channel19',
|
||||
'port-channel1044','port-channel2','port-channel104'
|
||||
|
||||
],[
|
||||
'port-channel2','port-channel19','port-channel104',
|
||||
'port-channel190','port-channel1044'
|
||||
],'port-channel');
|
||||
wrapTest(
|
||||
[
|
||||
'Serial1/1:5',
|
||||
'Serial2/0:5-Bearer Channel',
|
||||
'Serial2/0:20',
|
||||
'Serial1/1:5-Bearer Channel',
|
||||
'Serial1/1:0',
|
||||
'Serial2/0:21',
|
||||
'Serial2/0:5',
|
||||
'Serial2/0:20-Bearer Channel'
|
||||
],[
|
||||
'Serial1/1:0',
|
||||
'Serial1/1:5',
|
||||
'Serial1/1:5-Bearer Channel',
|
||||
'Serial2/0:5',
|
||||
'Serial2/0:5-Bearer Channel',
|
||||
'Serial2/0:20',
|
||||
'Serial2/0:20-Bearer Channel',
|
||||
'Serial2/0:21'
|
||||
],'serial');
|
||||
wrapTest(
|
||||
[
|
||||
'unrouted VLAN 990','unrouted VLAN 95',
|
||||
'unrouted VLAN 985','unrouted VLAN 99',
|
||||
'unrouted VLAN 950'
|
||||
|
||||
],[
|
||||
'unrouted VLAN 95','unrouted VLAN 99',
|
||||
'unrouted VLAN 950','unrouted VLAN 985',
|
||||
'unrouted VLAN 990'
|
||||
],'unrouted vlans');
|
||||
wrapTest(
|
||||
[
|
||||
'Vlan10','Vlan910','Vlan1','Vlan91'
|
||||
],[
|
||||
'Vlan1','Vlan10','Vlan91','Vlan910'
|
||||
],'vlans');
|
||||
wrapTest(
|
||||
[
|
||||
'voice-port 2/10','voice-port 2/1','voice-port 2/2',
|
||||
'voice-port 2/11'
|
||||
],[
|
||||
'voice-port 2/1','voice-port 2/2','voice-port 2/10',
|
||||
'voice-port 2/11',
|
||||
|
||||
],'voice-port');
|
||||
});
|
||||
|
||||
});
|
||||
var sortFn = jQuery.fn.dataTableExt.oSort["portsort-asc"];
|
||||
function wrapTest(origArray, sortArray, message) {
|
||||
deepEqual($.extend([], origArray).sort(sortFn), sortArray, (message ? message + ' - ' : '') + QUnit.jsDump.parse(origArray));
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="qunit"></div>
|
||||
<div id="qunit-fixture"></div>
|
||||
</body>
|
||||
</html>
|
||||
378
xt/js/qunit-tap.js
Normal file
378
xt/js/qunit-tap.js
Normal file
@@ -0,0 +1,378 @@
|
||||
/**
|
||||
* QUnit-TAP - A TAP Output Producer Plugin for QUnit
|
||||
*
|
||||
* https://github.com/twada/qunit-tap
|
||||
* version: 1.4.2
|
||||
*
|
||||
* Copyright (c) 2010-2014 Takuto Wada
|
||||
* Dual licensed under the MIT and GPLv2 licenses.
|
||||
* https://raw.github.com/twada/qunit-tap/master/MIT-LICENSE.txt
|
||||
* https://raw.github.com/twada/qunit-tap/master/GPL-LICENSE.txt
|
||||
*
|
||||
* A part of extend function is:
|
||||
* Copyright 2012 jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* A part of stripTags function is:
|
||||
* Copyright (c) 2005-2010 Sam Stephenson
|
||||
* Released under the MIT license.
|
||||
* http://prototypejs.org
|
||||
*/
|
||||
(function (root, factory) {
|
||||
'use strict';
|
||||
|
||||
// using returnExports UMD pattern
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
root.qunitTap = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
'use strict';
|
||||
|
||||
var qunitTapVersion = '1.4.2',
|
||||
slice = Array.prototype.slice;
|
||||
|
||||
// borrowed from qunit.js
|
||||
function extend (a, b) {
|
||||
var prop;
|
||||
for (prop in b) {
|
||||
if (b.hasOwnProperty(prop)) {
|
||||
if (typeof b[prop] === 'undefined') {
|
||||
delete a[prop];
|
||||
} else {
|
||||
a[prop] = b[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function indexOf (ary, element) {
|
||||
var i;
|
||||
for (i = 0; i < ary.length; i += 1) {
|
||||
if (ary[i] === element) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function removeElement (ary, element) {
|
||||
var index = indexOf(ary, element);
|
||||
if (index !== -1) {
|
||||
return ary.splice(index, 1);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function isPlanRequired (conf) {
|
||||
return (typeof conf !== 'undefined' && typeof conf.requireExpects !== 'undefined' && conf.requireExpects);
|
||||
}
|
||||
|
||||
function isPassed (details) {
|
||||
return !!(details.result);
|
||||
}
|
||||
|
||||
function isFailed (details) {
|
||||
return !(isPassed(details));
|
||||
}
|
||||
|
||||
function isAssertOkFailed (details) {
|
||||
return isFailed(details) && typeof details.expected === 'undefined' && typeof details.actual === 'undefined';
|
||||
}
|
||||
|
||||
// borrowed from prototype.js
|
||||
// not required since QUnit.log receives raw data (details). see jquery/qunit@c2cde34
|
||||
function stripTags (str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
return str.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
|
||||
}
|
||||
|
||||
function escapeLineEndings (str) {
|
||||
return str.replace(/(\r?\n)/g, '$&# ');
|
||||
}
|
||||
|
||||
function ltrim (str) {
|
||||
return str.replace(/^\s+/, '');
|
||||
}
|
||||
|
||||
function noop (obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function render (desc, fieldName, fieldValue, formatter) {
|
||||
desc.push(fieldName + ': ' + formatter(fieldValue));
|
||||
}
|
||||
|
||||
function renderIf (shouldRender, desc, fieldName, fieldValue, formatter) {
|
||||
if (!shouldRender || typeof fieldValue === 'undefined') {
|
||||
return;
|
||||
}
|
||||
render(desc, fieldName, fieldValue, formatter);
|
||||
}
|
||||
|
||||
function formatTestLine (testLine, rest) {
|
||||
if (!rest) {
|
||||
return testLine;
|
||||
}
|
||||
return testLine + ' - ' + escapeLineEndings(rest);
|
||||
}
|
||||
|
||||
var extractDetailsFrom = (function () {
|
||||
var detailsExtractor;
|
||||
|
||||
function setupExtractor (logArguments) {
|
||||
switch (logArguments.length) {
|
||||
case 1: // details
|
||||
detailsExtractor = function (args) { return args[0]; };
|
||||
break;
|
||||
case 2: // result, message(with tags)
|
||||
detailsExtractor = function (args) { return {result: args[0], message: stripTags(args[1])}; };
|
||||
break;
|
||||
case 3: // result, message, details
|
||||
detailsExtractor = function (args) { return args[2]; };
|
||||
break;
|
||||
default:
|
||||
throw new Error('QUnit-TAP does not support QUnit#log arguments like this.');
|
||||
}
|
||||
}
|
||||
|
||||
return function (logArguments) {
|
||||
if (detailsExtractor) {
|
||||
return detailsExtractor(logArguments);
|
||||
}
|
||||
setupExtractor(logArguments);
|
||||
return detailsExtractor(logArguments);
|
||||
};
|
||||
})();
|
||||
|
||||
var createCallbackAppenderFor = function (qu) {
|
||||
// detect QUnit's multipleCallbacks feature. see jquery/qunit@34f6bc1
|
||||
var isMultipleLoggingCallbacksSupported =
|
||||
(typeof qu.config !== 'undefined' &&
|
||||
typeof qu.config.log !== 'undefined' &&
|
||||
typeof qu.config.done !== 'undefined' &&
|
||||
typeof qu.config.testDone !== 'undefined' &&
|
||||
typeof qu.config.moduleStart !== 'undefined' &&
|
||||
typeof qu.config.testStart !== 'undefined');
|
||||
return function (subject, observer, event) {
|
||||
var originalLoggingCallback = subject[event],
|
||||
callback;
|
||||
if (isMultipleLoggingCallbacksSupported) {
|
||||
callback = function () {
|
||||
// make listener methods (moduleStart,testStart,log, ...) overridable.
|
||||
observer[event].apply(observer, slice.apply(arguments));
|
||||
};
|
||||
originalLoggingCallback(callback);
|
||||
} else if (typeof originalLoggingCallback === 'function') {
|
||||
// do not overwrite old-style logging callbacks
|
||||
callback = function () {
|
||||
var args = slice.apply(arguments);
|
||||
originalLoggingCallback.apply(subject, args);
|
||||
observer[event].apply(observer, args);
|
||||
};
|
||||
subject[event] = callback;
|
||||
}
|
||||
return callback;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* QUnit-TAP - A TAP Output Producer Plugin for QUnit
|
||||
* @param qunitObject QUnit object reference.
|
||||
* @param printLikeFunction print-like function for TAP output (assumes line-separator is added by this function for each call).
|
||||
* @param options configuration options to customize default behavior.
|
||||
* @return object to provide QUnit-TAP API and customization subject.
|
||||
*/
|
||||
function qunitTap(qunitObject, printLikeFunction, options) {
|
||||
if (!qunitObject) {
|
||||
throw new Error('should pass QUnit object reference. Please check QUnit\'s "require" path if you are using Node.js (or any CommonJS env).');
|
||||
} else if (typeof printLikeFunction !== 'function') {
|
||||
throw new Error('should pass print-like function');
|
||||
}
|
||||
|
||||
var qu = qunitObject,
|
||||
tap = {},
|
||||
jsDumpExists = (typeof qu.jsDump !== 'undefined' && typeof qu.jsDump.parse === 'function'),
|
||||
explain = (jsDumpExists ? function explain (obj) { return qu.jsDump.parse(obj); } : noop),
|
||||
deprecateOption = function deprecateOption (optionName, fallback) {
|
||||
// option deprecation and fallback function
|
||||
if (!options || typeof options !== 'object') {
|
||||
return;
|
||||
}
|
||||
if (typeof options[optionName] === 'undefined') {
|
||||
return;
|
||||
}
|
||||
printLikeFunction('# WARNING: Option "' + optionName + '" is deprecated and will be removed in future version.');
|
||||
fallback(options[optionName]);
|
||||
},
|
||||
targetEvents = [
|
||||
'moduleStart',
|
||||
'testStart',
|
||||
'log',
|
||||
'testDone',
|
||||
'done'
|
||||
],
|
||||
registeredCallbacks = {};
|
||||
|
||||
|
||||
tap.config = extend(
|
||||
{
|
||||
initialCount: 1,
|
||||
showModuleNameOnFailure: true,
|
||||
showTestNameOnFailure: true,
|
||||
showExpectationOnFailure: true,
|
||||
showSourceOnFailure: true
|
||||
},
|
||||
options
|
||||
);
|
||||
deprecateOption('noPlan', function (flag) {
|
||||
printLikeFunction('# Now QUnit-TAP works as with "noPlan: true" by default. If you want to delare plan explicitly, please use "QUnit.config.requireExpects" option instead.');
|
||||
tap.config.noPlan = flag;
|
||||
});
|
||||
deprecateOption('count', function (count) {
|
||||
tap.config.initialCount = (count + 1);
|
||||
});
|
||||
deprecateOption('showDetailsOnFailure', function (flag) {
|
||||
tap.config.showModuleNameOnFailure = flag;
|
||||
tap.config.showTestNameOnFailure = flag;
|
||||
tap.config.showExpectationOnFailure = flag;
|
||||
tap.config.showSourceOnFailure = flag;
|
||||
});
|
||||
tap.VERSION = qunitTapVersion;
|
||||
tap.puts = printLikeFunction;
|
||||
tap.count = tap.config.initialCount - 1;
|
||||
tap.expectedCount = tap.config.initialCount - 1;
|
||||
|
||||
function isEnabled (configName) {
|
||||
return tap.config[configName];
|
||||
}
|
||||
|
||||
function formatDetails (details) {
|
||||
if (isPassed(details)) {
|
||||
return details.message;
|
||||
}
|
||||
var desc = [];
|
||||
if (details.message) {
|
||||
desc.push(details.message);
|
||||
}
|
||||
if (isEnabled('showExpectationOnFailure') && !(isAssertOkFailed(details))) {
|
||||
render(desc, 'expected', details.expected, explain);
|
||||
render(desc, 'got', details.actual, explain);
|
||||
}
|
||||
renderIf(isEnabled('showTestNameOnFailure'), desc, 'test', details.name, noop);
|
||||
renderIf(isEnabled('showModuleNameOnFailure'), desc, 'module', details.module, noop);
|
||||
renderIf(isEnabled('showSourceOnFailure'), desc, 'source', details.source, ltrim);
|
||||
return desc.join(', ');
|
||||
}
|
||||
|
||||
function printPlanLine (toCount) {
|
||||
tap.puts(tap.config.initialCount + '..' + toCount);
|
||||
}
|
||||
|
||||
function unsubscribeEvent (eventName) {
|
||||
var listeners;
|
||||
if (indexOf(targetEvents, eventName) === -1) {
|
||||
return;
|
||||
}
|
||||
listeners = qu.config[eventName];
|
||||
if (typeof listeners === 'undefined') {
|
||||
return;
|
||||
}
|
||||
removeElement(listeners, registeredCallbacks[eventName]);
|
||||
}
|
||||
|
||||
function unsubscribeEvents (eventNames) {
|
||||
var i;
|
||||
for (i = 0; i < eventNames.length; i += 1) {
|
||||
unsubscribeEvent(eventNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
tap.explain = explain;
|
||||
|
||||
tap.note = function note (obj) {
|
||||
tap.puts(escapeLineEndings('# ' + obj));
|
||||
};
|
||||
|
||||
tap.diag = function diag (obj) {
|
||||
tap.note(obj);
|
||||
return false;
|
||||
};
|
||||
|
||||
tap.moduleStart = function moduleStart (arg) {
|
||||
var name = (typeof arg === 'string') ? arg : arg.name;
|
||||
tap.note('module: ' + name);
|
||||
};
|
||||
|
||||
tap.testStart = function testStart (arg) {
|
||||
var name = (typeof arg === 'string') ? arg : arg.name;
|
||||
tap.note('test: ' + name);
|
||||
};
|
||||
|
||||
tap.log = function log () {
|
||||
var details = extractDetailsFrom(arguments),
|
||||
testLine = '';
|
||||
tap.count += 1;
|
||||
if (isFailed(details)) {
|
||||
testLine += 'not ';
|
||||
}
|
||||
testLine += ('ok ' + tap.count);
|
||||
tap.puts(formatTestLine(testLine, formatDetails(details)));
|
||||
};
|
||||
|
||||
tap.testDone = function testDone () {
|
||||
if (isPlanRequired(qu.config)) {
|
||||
tap.expectedCount += qu.config.current.expected;
|
||||
}
|
||||
};
|
||||
|
||||
tap.done = function done () {
|
||||
if (typeof tap.config.noPlan !== 'undefined' && !(tap.config.noPlan)) {
|
||||
// Do nothing until removal of 'noPlan' option.
|
||||
} else if (isPlanRequired(qu.config)) {
|
||||
printPlanLine(tap.expectedCount);
|
||||
} else {
|
||||
printPlanLine(tap.count);
|
||||
}
|
||||
};
|
||||
|
||||
tap.unsubscribe = function unsubscribe () {
|
||||
if (typeof qu.config === 'undefined') {
|
||||
return;
|
||||
}
|
||||
if (arguments.length === 0) {
|
||||
unsubscribeEvents(targetEvents);
|
||||
} else {
|
||||
unsubscribeEvents(slice.apply(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
(function () {
|
||||
var appendCallback = createCallbackAppenderFor(qu),
|
||||
eventName, i, callback;
|
||||
for (i = 0; i < targetEvents.length; i += 1) {
|
||||
eventName = targetEvents[i];
|
||||
callback = appendCallback(qu, tap, eventName);
|
||||
registeredCallbacks[eventName] = callback;
|
||||
}
|
||||
})();
|
||||
|
||||
return tap;
|
||||
}
|
||||
|
||||
qunitTap.qunitTap = function () {
|
||||
throw new Error('[BC BREAK] Since 1.4.0, QUnit-TAP exports single qunitTap function as module.exports. Therefore, require("qunit-tap") returns qunitTap function itself. Please fix your code if you are using Node.js (or any CommonJS env).');
|
||||
};
|
||||
|
||||
// using substack pattern (export single function)
|
||||
return qunitTap;
|
||||
}));
|
||||
74
xt/js/run_qunit.js
Normal file
74
xt/js/run_qunit.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Wait until the test condition is true or a timeout occurs. Useful for waiting
|
||||
* on a server response or for a ui change (fadeIn, etc.) to occur.
|
||||
*
|
||||
* @param testFx javascript condition that evaluates to a boolean,
|
||||
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
||||
* as a callback function.
|
||||
* @param onReady what to do when testFx condition is fulfilled,
|
||||
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
|
||||
* as a callback function.
|
||||
* @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
|
||||
*/
|
||||
function waitFor(testFx, onReady, timeOutMillis) {
|
||||
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s
|
||||
start = new Date().getTime(),
|
||||
condition = false,
|
||||
interval = setInterval(function() {
|
||||
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
|
||||
// If not time-out yet and condition not yet fulfilled
|
||||
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
|
||||
} else {
|
||||
if(!condition) {
|
||||
// If condition still not fulfilled (timeout but condition is 'false')
|
||||
console.log("# 'waitFor()' timeout");
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
// Condition fulfilled (timeout and/or condition is 'true')
|
||||
// console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
|
||||
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
|
||||
clearInterval(interval); //< Stop this interval
|
||||
}
|
||||
}
|
||||
}, 100); //< repeat check every 250ms
|
||||
};
|
||||
|
||||
|
||||
if (phantom.args.length === 0 || phantom.args.length > 2) {
|
||||
console.log('Usage: run-qunit.js URL');
|
||||
phantom.exit(1);
|
||||
}
|
||||
|
||||
var page = new WebPage();
|
||||
|
||||
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
|
||||
page.onConsoleMessage = function(msg) {
|
||||
console.log(msg);
|
||||
};
|
||||
|
||||
page.open(phantom.args[0], function(status){
|
||||
if (status !== "success") {
|
||||
console.log("Unable to access network");
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
waitFor(function(){
|
||||
return page.evaluate(function(){
|
||||
var el = document.getElementById('qunit-testresult');
|
||||
if (el && el.innerText.match('completed')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}, function(){
|
||||
var failedNum = page.evaluate(function(){
|
||||
var el = document.getElementById('qunit-testresult');
|
||||
// console.log(el.innerText);
|
||||
try {
|
||||
return el.getElementsByClassName('failed')[0].innerHTML;
|
||||
} catch (e) { }
|
||||
return 10000;
|
||||
});
|
||||
phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user