diff options
author | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2016-03-23 22:49:18 +0100 |
---|---|---|
committer | Ulrich-Matthias Schäfer <ulima.ums@googlemail.com> | 2016-03-23 22:49:18 +0100 |
commit | 2b26d66fc4686c249ef27b2b8ca1bb20df7bb95d (patch) | |
tree | e4e9e05ed8859ff360f58b96a3292f83bf0a8ac5 | |
parent | 5bfb9127cb5126eb2f53fd50fba53711bc899a54 (diff) | |
download | svg.js-2b26d66fc4686c249ef27b2b8ca1bb20df7bb95d.tar.gz svg.js-2b26d66fc4686c249ef27b2b8ca1bb20df7bb95d.zip |
updated jasmine and specs
-rw-r--r-- | spec/SpecRunner.html | 90 | ||||
-rw-r--r-- | spec/index.html | 120 | ||||
-rw-r--r-- | spec/lib/jasmine-2.0.1/jasmine.css | 59 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/MIT.LICENSE | 20 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/boot.js (renamed from spec/lib/jasmine-2.0.1/boot.js) | 89 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/console.js (renamed from spec/lib/jasmine-2.0.1/console.js) | 31 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/jasmine-html.js (renamed from spec/lib/jasmine-2.0.1/jasmine-html.js) | 201 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/jasmine.css | 58 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/jasmine.js (renamed from spec/lib/jasmine-2.0.1/jasmine.js) | 2102 | ||||
-rw-r--r-- | spec/lib/jasmine-2.4.1/jasmine_favicon.png (renamed from spec/lib/jasmine-2.0.1/jasmine_favicon.png) | bin | 1486 -> 1486 bytes | |||
-rw-r--r-- | spec/spec/element.js | 2 | ||||
-rw-r--r-- | spec/spec/event.js | 4 | ||||
-rw-r--r-- | spec/spec/fx.js | 257 | ||||
-rw-r--r-- | spec/spec/sugar.js | 679 |
14 files changed, 2743 insertions, 969 deletions
diff --git a/spec/SpecRunner.html b/spec/SpecRunner.html new file mode 100644 index 0000000..81b64af --- /dev/null +++ b/spec/SpecRunner.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Jasmine Spec Runner v2.4.1</title> + + <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.4.1/jasmine_favicon.png"> + <link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css"> + + <script src="lib/jasmine-2.4.1/jasmine.js"></script> + <script src="lib/jasmine-2.4.1/jasmine-html.js"></script> + <script src="lib/jasmine-2.4.1/boot.js"></script> + + <style type="text/css" media="screen"> + #drawing { + width: 500px; + height: 500px; + position: fixed; + z-index: -1; + } + </style> + + <!-- include source files here... --> + <script src="../dist/svg.js" charset="utf-8"></script> + +</head> + +<body> + <svg height="0" width="0" id="inlineSVG"> + <desc>Some description</desc> + <path id="lineAB" d="M 100 350 l 150 -300" stroke="red" stroke-width="3" fill="none" /> + <path id="lineBC" d="M 250 50 l 150 300" stroke="red" stroke-width="3" fill="none" /> + <path d="M 175 200 l 150 0" stroke="green" stroke-width="3" fill="none" /> + <path d="M 100 350 q 150 -300 300 0" stroke="blue" stroke-width="5" fill="none" /> + <g stroke="black" stroke-width="3" fill="black" id="pointGroup"> + <circle id="pointA" cx="100" cy="350" r="3" /> + <circle id="pointB" cx="250" cy="50" r="3" /> + <circle id="pointC" cx="400" cy="350" r="3" /> + </g> + <g font-size="30" font="sans-serif" fill="black" stroke="none" text-anchor="middle" id="labelGroup"> + <text x="100" y="350" dx="-30">A</text> + <text x="250" y="50" dy="-10">B</text> + <text x="400" y="350" dx="30">C</text> + </g> + <polygon points="200,10 250,190 160,210" /> + <polyline points="20,20 40,25 60,40 80,120 120,140 200,180" /> + </svg> + + <!-- include spec files here... --> + <script src="spec/svg.js"></script> + <script src="spec/selector.js"></script> + <script src="spec/adopter.js"></script> + <script src="spec/regex.js"></script> + <script src="spec/container.js"></script> + <script src="spec/element.js"></script> + <script src="spec/memory.js"></script> + <script src="spec/arrange.js"></script> + <script src="spec/event.js"></script> + <script src="spec/boxes.js"></script> + <script src="spec/matrix.js"></script> + <script src="spec/rect.js"></script> + <script src="spec/circle.js"></script> + <script src="spec/ellipse.js"></script> + <script src="spec/line.js"></script> + <script src="spec/polyline.js"></script> + <script src="spec/polygon.js"></script> + <script src="spec/path.js"></script> + <script src="spec/marker.js"></script> + <script src="spec/image.js"></script> + <script src="spec/text.js"></script> + <script src="spec/textpath.js"></script> + <script src="spec/doc.js"></script> + <script src="spec/defs.js"></script> + <script src="spec/group.js"></script> + <script src="spec/set.js"></script> + <script src="spec/gradient.js"></script> + <script src="spec/pattern.js"></script> + <script src="spec/use.js"></script> + <script src="spec/bare.js"></script> + <script src="spec/mask.js"></script> + <script src="spec/clip.js"></script> + <script src="spec/color.js"></script> + <script src="spec/number.js"></script> + <script src="spec/array.js"></script> + <script src="spec/hyperlink.js"></script> + <script src="spec/fx.js"></script> + + <script type="text/javascript" src="spec/helper.js"></script> +</body> +</html> diff --git a/spec/index.html b/spec/index.html deleted file mode 100644 index 526cda6..0000000 --- a/spec/index.html +++ /dev/null @@ -1,120 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"> -<html> -<head> - <title>SVG.js - Jasmine Spec Runner</title> - - <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.1/jasmine_favicon.png"> - <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.1/jasmine.css"> - - <style type="text/css" media="screen"> - #drawing { - width: 500px; - height: 500px; - position: fixed; - z-index: -1; - } - </style> - -</head> - -<body> - <svg height="0" width="0" id="inlineSVG"> - <desc>Some description</desc> - <path id="lineAB" d="M 100 350 l 150 -300" stroke="red" stroke-width="3" fill="none" /> - <path id="lineBC" d="M 250 50 l 150 300" stroke="red" stroke-width="3" fill="none" /> - <path d="M 175 200 l 150 0" stroke="green" stroke-width="3" fill="none" /> - <path d="M 100 350 q 150 -300 300 0" stroke="blue" stroke-width="5" fill="none" /> - <g stroke="black" stroke-width="3" fill="black" id="pointGroup"> - <circle id="pointA" cx="100" cy="350" r="3" /> - <circle id="pointB" cx="250" cy="50" r="3" /> - <circle id="pointC" cx="400" cy="350" r="3" /> - </g> - <g font-size="30" font="sans-serif" fill="black" stroke="none" text-anchor="middle" id="labelGroup"> - <text x="100" y="350" dx="-30">A</text> - <text x="250" y="50" dy="-10">B</text> - <text x="400" y="350" dx="30">C</text> - </g> - <polygon points="200,10 250,190 160,210" /> - <polyline points="20,20 40,25 60,40 80,120 120,140 200,180" /> - </svg> -</body> - -<script src="lib/jasmine-2.0.1/jasmine.js"></script> -<script src="lib/jasmine-2.0.1/jasmine-html.js"></script> -<script src="lib/jasmine-2.0.1/boot.js"></script> - -<!-- include source files here... --> -<script src="../dist/svg.js" charset="utf-8"></script> - -<!-- include spec files here... --> -<script src="spec/svg.js"></script> -<script src="spec/selector.js"></script> -<script src="spec/adopter.js"></script> -<script src="spec/regex.js"></script> -<script src="spec/container.js"></script> -<script src="spec/element.js"></script> -<script src="spec/memory.js"></script> -<script src="spec/arrange.js"></script> -<script src="spec/event.js"></script> -<script src="spec/boxes.js"></script> -<script src="spec/matrix.js"></script> -<script src="spec/rect.js"></script> -<script src="spec/circle.js"></script> -<script src="spec/ellipse.js"></script> -<script src="spec/line.js"></script> -<script src="spec/polyline.js"></script> -<script src="spec/polygon.js"></script> -<script src="spec/path.js"></script> -<script src="spec/marker.js"></script> -<script src="spec/image.js"></script> -<script src="spec/text.js"></script> -<script src="spec/textpath.js"></script> -<script src="spec/doc.js"></script> -<script src="spec/defs.js"></script> -<script src="spec/group.js"></script> -<script src="spec/set.js"></script> -<script src="spec/gradient.js"></script> -<script src="spec/pattern.js"></script> -<script src="spec/use.js"></script> -<script src="spec/bare.js"></script> -<script src="spec/mask.js"></script> -<script src="spec/clip.js"></script> -<script src="spec/color.js"></script> -<script src="spec/number.js"></script> -<script src="spec/array.js"></script> -<script src="spec/hyperlink.js"></script> -<script src="spec/fx.js"></script> - -<script type="text/javascript"> - (function() { - var jasmineEnv = jasmine.getEnv(); - jasmineEnv.updateInterval = 1000; - - var htmlReporter = new jasmine.HtmlReporter(); - - jasmineEnv.addReporter(htmlReporter); - - jasmineEnv.specFilter = function(spec) { - return htmlReporter.specFilter(spec); - }; - - var currentWindowOnload = window.onload; - - window.onload = function() { - if (currentWindowOnload) { - currentWindowOnload(); - } - execJasmine(); - }; - - function execJasmine() { - jasmineEnv.execute(); - } - - })(); -</script> - -<script type="text/javascript" src="spec/helper.js"></script> - -</html> diff --git a/spec/lib/jasmine-2.0.1/jasmine.css b/spec/lib/jasmine-2.0.1/jasmine.css deleted file mode 100644 index c54ff30..0000000 --- a/spec/lib/jasmine-2.0.1/jasmine.css +++ /dev/null @@ -1,59 +0,0 @@ -body { overflow-y: scroll; } - -.jasmine_html-reporter { background-color: #eeeeee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } -.jasmine_html-reporter a { text-decoration: none; } -.jasmine_html-reporter a:hover { text-decoration: underline; } -.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } -.jasmine_html-reporter .banner, .jasmine_html-reporter .symbol-summary, .jasmine_html-reporter .summary, .jasmine_html-reporter .result-message, .jasmine_html-reporter .spec .description, .jasmine_html-reporter .spec-detail .description, .jasmine_html-reporter .alert .bar, .jasmine_html-reporter .stack-trace { padding-left: 9px; padding-right: 9px; } -.jasmine_html-reporter .banner { position: relative; } -.jasmine_html-reporter .banner .title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -webkit-background-size: 100%; -moz-background-size: 100%; -o-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } -.jasmine_html-reporter .banner .version { margin-left: 14px; position: relative; top: 6px; } -.jasmine_html-reporter .banner .duration { position: absolute; right: 14px; top: 6px; } -.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } -.jasmine_html-reporter .version { color: #aaaaaa; } -.jasmine_html-reporter .banner { margin-top: 14px; } -.jasmine_html-reporter .duration { color: #aaaaaa; float: right; } -.jasmine_html-reporter .symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } -.jasmine_html-reporter .symbol-summary li { display: inline-block; height: 8px; width: 14px; font-size: 16px; } -.jasmine_html-reporter .symbol-summary li.passed { font-size: 14px; } -.jasmine_html-reporter .symbol-summary li.passed:before { color: #007069; content: "\02022"; } -.jasmine_html-reporter .symbol-summary li.failed { line-height: 9px; } -.jasmine_html-reporter .symbol-summary li.failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; } -.jasmine_html-reporter .symbol-summary li.disabled { font-size: 14px; } -.jasmine_html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } -.jasmine_html-reporter .symbol-summary li.pending { line-height: 17px; } -.jasmine_html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } -.jasmine_html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } -.jasmine_html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } -.jasmine_html-reporter .bar.failed { background-color: #ca3a11; } -.jasmine_html-reporter .bar.passed { background-color: #007069; } -.jasmine_html-reporter .bar.skipped { background-color: #bababa; } -.jasmine_html-reporter .bar.menu { background-color: #fff; color: #aaaaaa; } -.jasmine_html-reporter .bar.menu a { color: #333333; } -.jasmine_html-reporter .bar a { color: white; } -.jasmine_html-reporter.spec-list .bar.menu.failure-list, .jasmine_html-reporter.spec-list .results .failures { display: none; } -.jasmine_html-reporter.failure-list .bar.menu.spec-list, .jasmine_html-reporter.failure-list .summary { display: none; } -.jasmine_html-reporter .running-alert { background-color: #666666; } -.jasmine_html-reporter .results { margin-top: 14px; } -.jasmine_html-reporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } -.jasmine_html-reporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } -.jasmine_html-reporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } -.jasmine_html-reporter.showDetails .summary { display: none; } -.jasmine_html-reporter.showDetails #details { display: block; } -.jasmine_html-reporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } -.jasmine_html-reporter .summary { margin-top: 14px; } -.jasmine_html-reporter .summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } -.jasmine_html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } -.jasmine_html-reporter .summary li.passed a { color: #007069; } -.jasmine_html-reporter .summary li.failed a { color: #ca3a11; } -.jasmine_html-reporter .summary li.empty a { color: #ba9d37; } -.jasmine_html-reporter .summary li.pending a { color: #ba9d37; } -.jasmine_html-reporter .description + .suite { margin-top: 0; } -.jasmine_html-reporter .suite { margin-top: 14px; } -.jasmine_html-reporter .suite a { color: #333333; } -.jasmine_html-reporter .failures .spec-detail { margin-bottom: 28px; } -.jasmine_html-reporter .failures .spec-detail .description { background-color: #ca3a11; } -.jasmine_html-reporter .failures .spec-detail .description a { color: white; } -.jasmine_html-reporter .result-message { padding-top: 14px; color: #333333; white-space: pre; } -.jasmine_html-reporter .result-message span.result { display: block; } -.jasmine_html-reporter .stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } diff --git a/spec/lib/jasmine-2.4.1/MIT.LICENSE b/spec/lib/jasmine-2.4.1/MIT.LICENSE new file mode 100644 index 0000000..aff8ed4 --- /dev/null +++ b/spec/lib/jasmine-2.4.1/MIT.LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008-2014 Pivotal Labs + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/spec/lib/jasmine-2.0.1/boot.js b/spec/lib/jasmine-2.4.1/boot.js index ec8baa0..a99774d 100644 --- a/spec/lib/jasmine-2.0.1/boot.js +++ b/spec/lib/jasmine-2.4.1/boot.js @@ -1,5 +1,5 @@ /** - Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. + Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project. If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms. @@ -32,77 +32,12 @@ * * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged. */ - var jasmineInterface = { - describe: function(description, specDefinitions) { - return env.describe(description, specDefinitions); - }, - - xdescribe: function(description, specDefinitions) { - return env.xdescribe(description, specDefinitions); - }, - - it: function(desc, func) { - return env.it(desc, func); - }, - - xit: function(desc, func) { - return env.xit(desc, func); - }, - - beforeEach: function(beforeEachFunction) { - return env.beforeEach(beforeEachFunction); - }, - - afterEach: function(afterEachFunction) { - return env.afterEach(afterEachFunction); - }, - - expect: function(actual) { - return env.expect(actual); - }, - - pending: function() { - return env.pending(); - }, - - spyOn: function(obj, methodName) { - return env.spyOn(obj, methodName); - }, - - jsApiReporter: new jasmine.JsApiReporter({ - timer: new jasmine.Timer() - }) - }; - - /** - * Add all of the Jasmine global/public interface to the proper global, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. - */ - if (typeof window == "undefined" && typeof exports == "object") { - extend(exports, jasmineInterface); - } else { - extend(window, jasmineInterface); - } - - /** - * Expose the interface for adding custom equality testers. - */ - jasmine.addCustomEqualityTester = function(tester) { - env.addCustomEqualityTester(tester); - }; + var jasmineInterface = jasmineRequire.interface(jasmine, env); /** - * Expose the interface for adding custom expectation matchers + * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`. */ - jasmine.addMatchers = function(matchers) { - return env.addMatchers(matchers); - }; - - /** - * Expose the mock interface for the JavaScript timeout functions - */ - jasmine.clock = function() { - return env.clock; - }; + extend(window, jasmineInterface); /** * ## Runner Parameters @@ -117,13 +52,27 @@ var catchingExceptions = queryString.getParam("catch"); env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions); + var throwingExpectationFailures = queryString.getParam("throwFailures"); + env.throwOnExpectationFailure(throwingExpectationFailures); + + var random = queryString.getParam("random"); + env.randomizeTests(random); + + var seed = queryString.getParam("seed"); + if (seed) { + env.seed(seed); + } + /** * ## Reporters * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any). */ var htmlReporter = new jasmine.HtmlReporter({ env: env, - onRaiseExceptionsClick: function() { queryString.setParam("catch", !env.catchingExceptions()); }, + onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); }, + onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); }, + onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); }, + addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); }, getContainer: function() { return document.body; }, createElement: function() { return document.createElement.apply(document, arguments); }, createTextNode: function() { return document.createTextNode.apply(document, arguments); }, diff --git a/spec/lib/jasmine-2.0.1/console.js b/spec/lib/jasmine-2.4.1/console.js index c54f72d..e154806 100644 --- a/spec/lib/jasmine-2.0.1/console.js +++ b/spec/lib/jasmine-2.4.1/console.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 Pivotal Labs +Copyright (c) 2008-2015 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -54,7 +54,10 @@ getJasmineRequireObj().ConsoleReporter = function() { red: '\x1B[31m', yellow: '\x1B[33m', none: '\x1B[0m' - }; + }, + failedSuites = []; + + print('ConsoleReporter is deprecated and will be removed in a future version.'); this.jasmineStarted = function() { specCount = 0; @@ -89,9 +92,12 @@ getJasmineRequireObj().ConsoleReporter = function() { printNewline(); var seconds = timer.elapsed() / 1000; print('Finished in ' + seconds + ' ' + plural('second', seconds)); - printNewline(); + for(i = 0; i < failedSuites.length; i++) { + suiteFailureDetails(failedSuites[i]); + } + onComplete(failureCount === 0); }; @@ -116,6 +122,13 @@ getJasmineRequireObj().ConsoleReporter = function() { } }; + this.suiteDone = function(result) { + if (result.failedExpectations && result.failedExpectations.length > 0) { + failureCount++; + failedSuites.push(result); + } + }; + return this; function printNewline() { @@ -154,11 +167,23 @@ getJasmineRequireObj().ConsoleReporter = function() { for (var i = 0; i < result.failedExpectations.length; i++) { var failedExpectation = result.failedExpectations[i]; printNewline(); + print(indent(failedExpectation.message, 2)); print(indent(failedExpectation.stack, 2)); } printNewline(); } + + function suiteFailureDetails(result) { + for (var i = 0; i < result.failedExpectations.length; i++) { + printNewline(); + print(colored('red', 'An error was thrown in an afterAll')); + printNewline(); + print(colored('red', 'AfterAll ' + result.failedExpectations[i].message)); + + } + printNewline(); + } } return ConsoleReporter; diff --git a/spec/lib/jasmine-2.0.1/jasmine-html.js b/spec/lib/jasmine-2.4.1/jasmine-html.js index 9d95903..da23532 100644 --- a/spec/lib/jasmine-2.0.1/jasmine-html.js +++ b/spec/lib/jasmine-2.4.1/jasmine-html.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 Pivotal Labs +Copyright (c) 2008-2015 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -40,30 +40,32 @@ jasmineRequire.HtmlReporter = function(j$) { createElement = options.createElement, createTextNode = options.createTextNode, onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {}, + onThrowExpectationsClick = options.onThrowExpectationsClick || function() {}, + onRandomClick = options.onRandomClick || function() {}, + addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, timer = options.timer || noopTimer, results = [], specsExecuted = 0, failureCount = 0, pendingSpecCount = 0, htmlReporterMain, - symbols; + symbols, + failedSuites = []; this.initialize = function() { clearPrior(); htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'}, - createDom('div', {className: 'banner'}, - createDom('a', {className: 'title', href: 'http://jasmine.github.io/', target: '_blank'}), - createDom('span', {className: 'version'}, j$.version) + createDom('div', {className: 'jasmine-banner'}, + createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}), + createDom('span', {className: 'jasmine-version'}, j$.version) ), - createDom('ul', {className: 'symbol-summary'}), - createDom('div', {className: 'alert'}), - createDom('div', {className: 'results'}, - createDom('div', {className: 'failures'}) + createDom('ul', {className: 'jasmine-symbol-summary'}), + createDom('div', {className: 'jasmine-alert'}), + createDom('div', {className: 'jasmine-results'}, + createDom('div', {className: 'jasmine-failures'}) ) ); getContainer().appendChild(htmlReporterMain); - - symbols = find('.symbol-summary'); }; var totalSpecsDefined; @@ -72,7 +74,7 @@ jasmineRequire.HtmlReporter = function(j$) { timer.start(); }; - var summary = createDom('div', {className: 'summary'}); + var summary = createDom('div', {className: 'jasmine-summary'}); var topResults = new j$.ResultsNode({}, '', null), currentParent = topResults; @@ -83,6 +85,10 @@ jasmineRequire.HtmlReporter = function(j$) { }; this.suiteDone = function(result) { + if (result.status == 'failed') { + failedSuites.push(result); + } + if (currentParent == topResults) { return; } @@ -96,7 +102,7 @@ jasmineRequire.HtmlReporter = function(j$) { var failures = []; this.specDone = function(result) { - if(noExpectations(result) && console && console.error) { + if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') { console.error('Spec \'' + result.fullName + '\' has no expectations.'); } @@ -104,8 +110,12 @@ jasmineRequire.HtmlReporter = function(j$) { specsExecuted++; } + if (!symbols){ + symbols = find('.jasmine-symbol-summary'); + } + symbols.appendChild(createDom('li', { - className: noExpectations(result) ? 'empty' : result.status, + className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status, id: 'spec_' + result.id, title: result.fullName } @@ -115,18 +125,18 @@ jasmineRequire.HtmlReporter = function(j$) { failureCount++; var failure = - createDom('div', {className: 'spec-detail failed'}, - createDom('div', {className: 'description'}, + createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, + createDom('div', {className: 'jasmine-description'}, createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName) ), - createDom('div', {className: 'messages'}) + createDom('div', {className: 'jasmine-messages'}) ); var messages = failure.childNodes[1]; for (var i = 0; i < result.failedExpectations.length; i++) { var expectation = result.failedExpectations[i]; - messages.appendChild(createDom('div', {className: 'result-message'}, expectation.message)); - messages.appendChild(createDom('div', {className: 'stack-trace'}, expectation.stack)); + messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); + messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); } failures.push(failure); @@ -137,48 +147,106 @@ jasmineRequire.HtmlReporter = function(j$) { } }; - this.jasmineDone = function() { - var banner = find('.banner'); - banner.appendChild(createDom('span', {className: 'duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); + this.jasmineDone = function(doneResult) { + var banner = find('.jasmine-banner'); + var alert = find('.jasmine-alert'); + var order = doneResult && doneResult.order; + alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); + + banner.appendChild( + createDom('div', { className: 'jasmine-run-options' }, + createDom('span', { className: 'jasmine-trigger' }, 'Options'), + createDom('div', { className: 'jasmine-payload' }, + createDom('div', { className: 'jasmine-exceptions' }, + createDom('input', { + className: 'jasmine-raise', + id: 'jasmine-raise-exceptions', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), + createDom('div', { className: 'jasmine-throw-failures' }, + createDom('input', { + className: 'jasmine-throw', + id: 'jasmine-throw-failures', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')), + createDom('div', { className: 'jasmine-random-order' }, + createDom('input', { + className: 'jasmine-random', + id: 'jasmine-random-order', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order')) + ) + )); + + var raiseCheckbox = find('#jasmine-raise-exceptions'); - var alert = find('.alert'); + raiseCheckbox.checked = !env.catchingExceptions(); + raiseCheckbox.onclick = onRaiseExceptionsClick; - alert.appendChild(createDom('span', { className: 'exceptions' }, - createDom('label', { className: 'label', 'for': 'raise-exceptions' }, 'raise exceptions'), - createDom('input', { - className: 'raise', - id: 'raise-exceptions', - type: 'checkbox' - }) - )); - var checkbox = find('#raise-exceptions'); + var throwCheckbox = find('#jasmine-throw-failures'); + throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.onclick = onThrowExpectationsClick; - checkbox.checked = !env.catchingExceptions(); - checkbox.onclick = onRaiseExceptionsClick; + var randomCheckbox = find('#jasmine-random-order'); + randomCheckbox.checked = env.randomTests(); + randomCheckbox.onclick = onRandomClick; + + var optionsMenu = find('.jasmine-run-options'), + optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'), + optionsPayload = optionsMenu.querySelector('.jasmine-payload'), + isOpen = /\bjasmine-open\b/; + + optionsTrigger.onclick = function() { + if (isOpen.test(optionsPayload.className)) { + optionsPayload.className = optionsPayload.className.replace(isOpen, ''); + } else { + optionsPayload.className += ' jasmine-open'; + } + }; if (specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; alert.appendChild( - createDom('span', {className: 'bar skipped'}, + createDom('span', {className: 'jasmine-bar jasmine-skipped'}, createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage) ) ); } var statusBarMessage = ''; - var statusBarClassName = 'bar '; + var statusBarClassName = 'jasmine-bar '; if (totalSpecsDefined > 0) { statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount); if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); } - statusBarClassName += (failureCount > 0) ? 'failed' : 'passed'; + statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed'; } else { - statusBarClassName += 'skipped'; + statusBarClassName += 'jasmine-skipped'; statusBarMessage += 'No specs found'; } - alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage)); + var seedBar; + if (order && order.random) { + seedBar = createDom('span', {className: 'jasmine-seed-bar'}, + ', randomized with seed ', + createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed) + ); + } + + alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar)); - var results = find('.results'); + for(i = 0; i < failedSuites.length; i++) { + var failedSuite = failedSuites[i]; + for(var j = 0; j < failedSuite.failedExpectations.length; j++) { + var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message; + var errorBarClassName = 'jasmine-bar jasmine-errored'; + alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage)); + } + } + + var results = find('.jasmine-results'); results.appendChild(summary); summaryList(topResults, summary); @@ -188,8 +256,8 @@ jasmineRequire.HtmlReporter = function(j$) { for (var i = 0; i < resultsTree.children.length; i++) { var resultNode = resultsTree.children[i]; if (resultNode.type == 'suite') { - var suiteListNode = createDom('ul', {className: 'suite', id: 'suite-' + resultNode.result.id}, - createDom('li', {className: 'suite-detail'}, + var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id}, + createDom('li', {className: 'jasmine-suite-detail'}, createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description) ) ); @@ -198,17 +266,20 @@ jasmineRequire.HtmlReporter = function(j$) { domParent.appendChild(suiteListNode); } if (resultNode.type == 'spec') { - if (domParent.getAttribute('class') != 'specs') { - specListNode = createDom('ul', {className: 'specs'}); + if (domParent.getAttribute('class') != 'jasmine-specs') { + specListNode = createDom('ul', {className: 'jasmine-specs'}); domParent.appendChild(specListNode); } var specDescription = resultNode.result.description; if(noExpectations(resultNode.result)) { specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; } + if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { + specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; + } specListNode.appendChild( createDom('li', { - className: resultNode.result.status, + className: 'jasmine-' + resultNode.result.status, id: 'spec-' + resultNode.result.id }, createDom('a', {href: specHref(resultNode.result)}, specDescription) @@ -220,24 +291,24 @@ jasmineRequire.HtmlReporter = function(j$) { if (failures.length) { alert.appendChild( - createDom('span', {className: 'menu bar spec-list'}, + createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'}, createDom('span', {}, 'Spec List | '), - createDom('a', {className: 'failures-menu', href: '#'}, 'Failures'))); + createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures'))); alert.appendChild( - createDom('span', {className: 'menu bar failure-list'}, - createDom('a', {className: 'spec-list-menu', href: '#'}, 'Spec List'), + createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'}, + createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'), createDom('span', {}, ' | Failures '))); - find('.failures-menu').onclick = function() { - setMenuModeTo('failure-list'); + find('.jasmine-failures-menu').onclick = function() { + setMenuModeTo('jasmine-failure-list'); }; - find('.spec-list-menu').onclick = function() { - setMenuModeTo('spec-list'); + find('.jasmine-spec-list-menu').onclick = function() { + setMenuModeTo('jasmine-spec-list'); }; - setMenuModeTo('failure-list'); + setMenuModeTo('jasmine-failure-list'); - var failureNode = find('.failures'); + var failureNode = find('.jasmine-failures'); for (var i = 0; i < failures.length; i++) { failureNode.appendChild(failures[i]); } @@ -253,7 +324,7 @@ jasmineRequire.HtmlReporter = function(j$) { function clearPrior() { // return the reporter var oldReporter = find(''); - + if(oldReporter) { getContainer().removeChild(oldReporter); } @@ -292,7 +363,15 @@ jasmineRequire.HtmlReporter = function(j$) { } function specHref(result) { - return '?spec=' + encodeURIComponent(result.fullName); + return addToExistingQueryString('spec', result.fullName); + } + + function seedHref(seed) { + return addToExistingQueryString('seed', seed); + } + + function defaultQueryString(key, value) { + return '?' + key + '=' + value; } function setMenuModeTo(mode) { @@ -344,10 +423,14 @@ jasmineRequire.ResultsNode = function() { jasmineRequire.QueryString = function() { function QueryString(options) { - this.setParam = function(key, value) { + this.navigateWithNewParam = function(key, value) { + options.getWindowLocation().search = this.fullStringWithNewParam(key, value); + }; + + this.fullStringWithNewParam = function(key, value) { var paramMap = queryStringToParamMap(); paramMap[key] = value; - options.getWindowLocation().search = toQueryString(paramMap); + return toQueryString(paramMap); }; this.getParam = function(key) { diff --git a/spec/lib/jasmine-2.4.1/jasmine.css b/spec/lib/jasmine-2.4.1/jasmine.css new file mode 100644 index 0000000..6319982 --- /dev/null +++ b/spec/lib/jasmine-2.4.1/jasmine.css @@ -0,0 +1,58 @@ +body { overflow-y: scroll; } + +.jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; } +.jasmine_html-reporter a { text-decoration: none; } +.jasmine_html-reporter a:hover { text-decoration: underline; } +.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; } +.jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; } +.jasmine_html-reporter .jasmine-banner { position: relative; } +.jasmine_html-reporter .jasmine-banner .jasmine-title { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAAAZCAMAAACGusnyAAACdlBMVEX/////AP+AgICqVaqAQICZM5mAVYCSSZKAQICOOY6ATYCLRouAQICJO4mSSYCIRIiPQICHPIeOR4CGQ4aMQICGPYaLRoCFQ4WKQICPPYWJRYCOQoSJQICNPoSIRICMQoSHQICHRICKQoOHQICKPoOJO4OJQYOMQICMQ4CIQYKLQICIPoKLQ4CKQICNPoKJQISMQ4KJQoSLQYKJQISLQ4KIQoSKQYKIQICIQISMQoSKQYKLQIOLQoOJQYGLQIOKQIOMQoGKQYOLQYGKQIOLQoGJQYOJQIOKQYGJQIOKQoGKQIGLQIKLQ4KKQoGLQYKJQIGKQYKJQIGKQIKJQoGKQYKLQIGKQYKLQIOJQoKKQoOJQYKKQIOJQoKKQoOKQIOLQoKKQYOLQYKJQIOKQoKKQYKKQoKJQYOKQYKLQIOKQoKLQYOKQYKLQIOJQoGKQYKJQYGJQoGKQYKLQoGLQYGKQoGJQYKKQYGJQIKKQoGJQYKLQIKKQYGLQYKKQYGKQYGKQYKJQYOKQoKJQYOKQYKLQYOLQYOKQYKLQYOKQoKKQYKKQYOKQYOJQYKKQYKLQYKKQIKKQoKKQYKKQYKKQoKJQIKKQYKLQYKKQYKKQIKKQYKKQYKKQYKKQIKKQYKJQYGLQYGKQYKKQYKKQYGKQIKKQYGKQYOJQoKKQYOLQYKKQYOKQoKKQYKKQoKKQYKKQYKJQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKJQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKLQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKKQYKmIDpEAAAA0XRSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAiIyQlJycoKissLS4wMTQ1Njc4OTo7PDw+P0BCQ0RISUpLTE1OUFNUVVdYWFlaW15fYGFiY2ZnaGlqa2xtb3BxcnN0dnh5ent8fX5/gIGChIWIioyNjo+QkZOUlZaYmZqbnJ2eoKGio6WmqKmsra6vsLGztre4ubq7vL2+wMHDxMjJysvNzs/Q0dLU1tfY2dvc3t/g4eLj5ebn6Onq6+zt7u/w8vP09fb3+Pn6+/z9/vkVQXAAAAMaSURBVHhe5dXxV1N1GMfxz2ABbDgIAm5VDJOyVDIJLUMaVpBWUZUaGbmqoGpZRSiGiRWp6KoZ5AB0ZY50RImZQIlahKkMYXv/R90dBvET/rJfOr3Ouc8v99zPec59zvf56j+vYKlViSf7250X4Mr3O29Tgq08BdGB4DhcekEJ5YkQKFsgWZdtj9JpV+I8xPjLFqkrsEIqO8PHSpis36jWazcqjEsfJjkvRssVU37SdIOu4XCf5vEJPsnwJpnRNU9JmxhMk8l1gehIrq7hTFjzOD+Vf88629qKMJVNltInFeRexRQyJlNeqd1iGDlSzrIUIyXbyFfm3RYprcQRe7lqtWyGYbfc6dT0R2vmdOOkX3u55C1rP37ftiH+tDby4r/RBT0w8TyEkr+epB9XgPDmSYYWbrhCuFYaIyw3fDQAXTnSkh+ANofiHmWf9l+FY1I90FdQTetstO00o23novzVsJ7uB3/C5TkbjRwZ5JerwV4iRWq9HFbFMaK/d0TYqayRiQPuIxxS3Bu8JWU90/60tKi7vkhaznez0a/TbVOKj5CaOZh6fWG6/Lyv9B/ZLR1gw/S/fpbeVD3MCW1li6SvWDOn65tr99/uvWtBS0XDm4s1t+sOHpG0kpBKx/l77wOSnxLpcx6TXmXLTPQOKYOf9Q1dfr8/SJ2mFdCvl1Yl93DiHUZvXeLJbGSzYu5gVJ2slbSakOR8dxCq5adQ2oFLqsE9Ex3L4qQO0eOPeU5x56bypXp4onSEb5OkICX6lDat55TeoztNKQcJaakrz9KCb95oD69IKq+yKW4XPjknaS52V0TZqE2cTtXjcHSCRmUO88e+85hj3EP74i9p8pylw7lxgMDyyl6OV7ZejnjNMfatu87LxRbH0IS35gt2a4ZjmGpVBdKK3Wr6INk8jWWSGqbA55CKgjBRC6E9w78ydTg3ABS3AFV1QN0Y4Aa2pgEjWnQURj9L0ayK6R2ysEqxHUKzYnLvvyU+i9KM2JHJzE4vyZOyDcOwOsySajeLPc8sNvPJkFlyJd20wpqAzZeAfZ3oWybxd+P/3j+SG3uSBdf2VQAAAABJRU5ErkJggg==') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; } +.jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; } +.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; } +.jasmine_html-reporter .jasmine-version { color: #aaa; } +.jasmine_html-reporter .jasmine-banner { margin-top: 14px; } +.jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; } +.jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; } +.jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "\02022"; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled { font-size: 14px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled:before { color: #bababa; content: "\02022"; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; } +.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "\02022"; } +.jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; } +.jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; } +.jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; } +.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; } +.jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +.jasmine_html-reporter .jasmine-bar.jasmine-failed { background-color: #ca3a11; } +.jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; } +.jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; } +.jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; } +.jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; } +.jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; } +.jasmine_html-reporter .jasmine-bar a { color: white; } +.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; } +.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; } +.jasmine_html-reporter .jasmine-results { margin-top: 14px; } +.jasmine_html-reporter .jasmine-summary { margin-top: 14px; } +.jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; } +.jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; } +.jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; } +.jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; } +.jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; } +.jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; } +.jasmine_html-reporter .jasmine-summary li.jasmine-disabled a { color: #bababa; } +.jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; } +.jasmine_html-reporter .jasmine-suite { margin-top: 14px; } +.jasmine_html-reporter .jasmine-suite a { color: #333; } +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; } +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; } +.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; } +.jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre; } +.jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; } +.jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; } diff --git a/spec/lib/jasmine-2.0.1/jasmine.js b/spec/lib/jasmine-2.4.1/jasmine.js index c943db1..bea469d 100644 --- a/spec/lib/jasmine-2.0.1/jasmine.js +++ b/spec/lib/jasmine-2.4.1/jasmine.js @@ -1,5 +1,5 @@ /* -Copyright (c) 2008-2014 Pivotal Labs +Copyright (c) 2008-2015 Pivotal Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -20,45 +20,67 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -function getJasmineRequireObj() { +var getJasmineRequireObj = (function (jasmineGlobal) { + var jasmineRequire; + if (typeof module !== 'undefined' && module.exports) { - return exports; + if (typeof global !== 'undefined') { + jasmineGlobal = global; + } else { + jasmineGlobal = {}; + } + jasmineRequire = exports; } else { - window.jasmineRequire = window.jasmineRequire || {}; - return window.jasmineRequire; + if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') { + jasmineGlobal = window; + } + jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {}; } -} -getJasmineRequireObj().core = function(jRequire) { - var j$ = {}; - - jRequire.base(j$); - j$.util = jRequire.util(); - j$.Any = jRequire.Any(); - j$.CallTracker = jRequire.CallTracker(); - j$.MockDate = jRequire.MockDate(); - j$.Clock = jRequire.Clock(); - j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); - j$.Env = jRequire.Env(j$); - j$.ExceptionFormatter = jRequire.ExceptionFormatter(); - j$.Expectation = jRequire.Expectation(); - j$.buildExpectationResult = jRequire.buildExpectationResult(); - j$.JsApiReporter = jRequire.JsApiReporter(); - j$.matchersUtil = jRequire.matchersUtil(j$); - j$.ObjectContaining = jRequire.ObjectContaining(j$); - j$.pp = jRequire.pp(j$); - j$.QueueRunner = jRequire.QueueRunner(j$); - j$.ReportDispatcher = jRequire.ReportDispatcher(); - j$.Spec = jRequire.Spec(j$); - j$.SpyStrategy = jRequire.SpyStrategy(); - j$.Suite = jRequire.Suite(); - j$.Timer = jRequire.Timer(); - j$.version = jRequire.version(); - - j$.matchers = jRequire.requireMatchers(jRequire, j$); - - return j$; -}; + function getJasmineRequire() { + return jasmineRequire; + } + + getJasmineRequire().core = function(jRequire) { + var j$ = {}; + + jRequire.base(j$, jasmineGlobal); + j$.util = jRequire.util(); + j$.errors = jRequire.errors(); + j$.Any = jRequire.Any(j$); + j$.Anything = jRequire.Anything(j$); + j$.CallTracker = jRequire.CallTracker(); + j$.MockDate = jRequire.MockDate(); + j$.Clock = jRequire.Clock(); + j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); + j$.Env = jRequire.Env(j$); + j$.ExceptionFormatter = jRequire.ExceptionFormatter(); + j$.Expectation = jRequire.Expectation(); + j$.buildExpectationResult = jRequire.buildExpectationResult(); + j$.JsApiReporter = jRequire.JsApiReporter(); + j$.matchersUtil = jRequire.matchersUtil(j$); + j$.ObjectContaining = jRequire.ObjectContaining(j$); + j$.ArrayContaining = jRequire.ArrayContaining(j$); + j$.pp = jRequire.pp(j$); + j$.QueueRunner = jRequire.QueueRunner(j$); + j$.ReportDispatcher = jRequire.ReportDispatcher(); + j$.Spec = jRequire.Spec(j$); + j$.SpyRegistry = jRequire.SpyRegistry(j$); + j$.SpyStrategy = jRequire.SpyStrategy(); + j$.StringMatching = jRequire.StringMatching(j$); + j$.Suite = jRequire.Suite(j$); + j$.Timer = jRequire.Timer(); + j$.TreeProcessor = jRequire.TreeProcessor(); + j$.version = jRequire.version(); + j$.Order = jRequire.Order(); + + j$.matchers = jRequire.requireMatchers(jRequire, j$); + + return j$; + }; + + return getJasmineRequire; +})(this); getJasmineRequireObj().requireMatchers = function(jRequire, j$) { var availableMatchers = [ @@ -76,6 +98,7 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { 'toEqual', 'toHaveBeenCalled', 'toHaveBeenCalledWith', + 'toHaveBeenCalledTimes', 'toMatch', 'toThrow', 'toThrowError' @@ -90,108 +113,128 @@ getJasmineRequireObj().requireMatchers = function(jRequire, j$) { return matchers; }; -getJasmineRequireObj().base = (function (jasmineGlobal) { - if (typeof module !== 'undefined' && module.exports) { - jasmineGlobal = global; - } +getJasmineRequireObj().base = function(j$, jasmineGlobal) { + j$.unimplementedMethod_ = function() { + throw new Error('unimplemented method'); + }; - return function(j$) { - j$.unimplementedMethod_ = function() { - throw new Error('unimplemented method'); - }; + j$.MAX_PRETTY_PRINT_DEPTH = 40; + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; + j$.DEFAULT_TIMEOUT_INTERVAL = 5000; - j$.MAX_PRETTY_PRINT_DEPTH = 40; - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100; - j$.DEFAULT_TIMEOUT_INTERVAL = 5000; + j$.getGlobal = function() { + return jasmineGlobal; + }; - j$.getGlobal = function() { - return jasmineGlobal; - }; + j$.getEnv = function(options) { + var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); + //jasmine. singletons in here (setTimeout blah blah). + return env; + }; - j$.getEnv = function(options) { - var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options); - //jasmine. singletons in here (setTimeout blah blah). - return env; - }; + j$.isArray_ = function(value) { + return j$.isA_('Array', value); + }; - j$.isArray_ = function(value) { - return j$.isA_('Array', value); - }; + j$.isString_ = function(value) { + return j$.isA_('String', value); + }; - j$.isString_ = function(value) { - return j$.isA_('String', value); - }; + j$.isNumber_ = function(value) { + return j$.isA_('Number', value); + }; - j$.isNumber_ = function(value) { - return j$.isA_('Number', value); - }; + j$.isA_ = function(typeName, value) { + return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; + }; - j$.isA_ = function(typeName, value) { - return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; - }; + j$.isDomNode = function(obj) { + return obj.nodeType > 0; + }; - j$.isDomNode = function(obj) { - return obj.nodeType > 0; - }; + j$.fnNameFor = function(func) { + return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; + }; - j$.any = function(clazz) { - return new j$.Any(clazz); - }; + j$.any = function(clazz) { + return new j$.Any(clazz); + }; - j$.objectContaining = function(sample) { - return new j$.ObjectContaining(sample); - }; + j$.anything = function() { + return new j$.Anything(); + }; - j$.createSpy = function(name, originalFn) { + j$.objectContaining = function(sample) { + return new j$.ObjectContaining(sample); + }; - var spyStrategy = new j$.SpyStrategy({ - name: name, - fn: originalFn, - getSpy: function() { return spy; } - }), - callTracker = new j$.CallTracker(), - spy = function() { - callTracker.track({ - object: this, - args: Array.prototype.slice.apply(arguments) - }); - return spyStrategy.exec.apply(this, arguments); + j$.stringMatching = function(expected) { + return new j$.StringMatching(expected); + }; + + j$.arrayContaining = function(sample) { + return new j$.ArrayContaining(sample); + }; + + j$.createSpy = function(name, originalFn) { + + var spyStrategy = new j$.SpyStrategy({ + name: name, + fn: originalFn, + getSpy: function() { return spy; } + }), + callTracker = new j$.CallTracker(), + spy = function() { + var callData = { + object: this, + args: Array.prototype.slice.apply(arguments) }; - for (var prop in originalFn) { - if (prop === 'and' || prop === 'calls') { - throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon'); - } + callTracker.track(callData); + var returnValue = spyStrategy.exec.apply(this, arguments); + callData.returnValue = returnValue; + + return returnValue; + }; - spy[prop] = originalFn[prop]; + for (var prop in originalFn) { + if (prop === 'and' || prop === 'calls') { + throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon'); } - spy.and = spyStrategy; - spy.calls = callTracker; + spy[prop] = originalFn[prop]; + } - return spy; - }; + spy.and = spyStrategy; + spy.calls = callTracker; - j$.isSpy = function(putativeSpy) { - if (!putativeSpy) { - return false; - } - return putativeSpy.and instanceof j$.SpyStrategy && - putativeSpy.calls instanceof j$.CallTracker; - }; + return spy; + }; - j$.createSpyObj = function(baseName, methodNames) { - if (!j$.isArray_(methodNames) || methodNames.length === 0) { - throw 'createSpyObj requires a non-empty array of method names to create spies for'; - } - var obj = {}; - for (var i = 0; i < methodNames.length; i++) { - obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); - } - return obj; - }; + j$.isSpy = function(putativeSpy) { + if (!putativeSpy) { + return false; + } + return putativeSpy.and instanceof j$.SpyStrategy && + putativeSpy.calls instanceof j$.CallTracker; }; -})(this); + + j$.createSpyObj = function(baseName, methodNames) { + if (j$.isArray_(baseName) && j$.util.isUndefined(methodNames)) { + methodNames = baseName; + baseName = 'unknown'; + } + + if (!j$.isArray_(methodNames) || methodNames.length === 0) { + throw 'createSpyObj requires a non-empty array of method names to create spies for'; + } + var obj = {}; + for (var i = 0; i < methodNames.length; i++) { + obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]); + } + return obj; + }; +}; getJasmineRequireObj().util = function() { @@ -228,13 +271,28 @@ getJasmineRequireObj().util = function() { util.arrayContains = function(array, search) { var i = array.length; while (i--) { - if (array[i] == search) { + if (array[i] === search) { return true; } } return false; }; + util.clone = function(obj) { + if (Object.prototype.toString.apply(obj) === '[object Array]') { + return obj.slice(); + } + + var cloned = {}; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + cloned[prop] = obj[prop]; + } + } + + return cloned; + }; + return util; }; @@ -244,17 +302,17 @@ getJasmineRequireObj().Spec = function(j$) { this.resultCallback = attrs.resultCallback || function() {}; this.id = attrs.id; this.description = attrs.description || ''; - this.fn = attrs.fn; - this.beforeFns = attrs.beforeFns || function() { return []; }; - this.afterFns = attrs.afterFns || function() { return []; }; + this.queueableFn = attrs.queueableFn; + this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; }; + this.userContext = attrs.userContext || function() { return {}; }; this.onStart = attrs.onStart || function() {}; - this.exceptionFormatter = attrs.exceptionFormatter || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; this.expectationResultFactory = attrs.expectationResultFactory || function() { }; this.queueRunnerFactory = attrs.queueRunnerFactory || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; - if (!this.fn) { + if (!this.queueableFn.fn) { this.pend(); } @@ -263,16 +321,21 @@ getJasmineRequireObj().Spec = function(j$) { description: this.description, fullName: this.getFullName(), failedExpectations: [], - passedExpectations: [] + passedExpectations: [], + pendingReason: '' }; } - Spec.prototype.addExpectationResult = function(passed, data) { + Spec.prototype.addExpectationResult = function(passed, data, isError) { var expectationResult = this.expectationResultFactory(data); if (passed) { this.result.passedExpectations.push(expectationResult); } else { this.result.failedExpectations.push(expectationResult); + + if (this.throwOnExpectationFailure && !isError) { + throw new j$.errors.ExpectationFailed(); + } } }; @@ -280,42 +343,28 @@ getJasmineRequireObj().Spec = function(j$) { return this.expectationFactory(actual, this); }; - Spec.prototype.execute = function(onComplete) { + Spec.prototype.execute = function(onComplete, enabled) { var self = this; this.onStart(this); - if (this.markedPending || this.disabled) { - complete(); + if (!this.isExecutable() || this.markedPending || enabled === false) { + complete(enabled); return; } - var allFns = this.beforeFns().concat(this.fn).concat(this.afterFns()); + var fns = this.beforeAndAfterFns(); + var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters); this.queueRunnerFactory({ - fns: allFns, - onException: onException, + queueableFns: allFns, + onException: function() { self.onException.apply(self, arguments); }, onComplete: complete, - enforceTimeout: function() { return true; } + userContext: this.userContext() }); - function onException(e) { - if (Spec.isPendingSpecException(e)) { - self.pend(); - return; - } - - self.addExpectationResult(false, { - matcherName: '', - passed: false, - expected: '', - actual: '', - error: e - }); - } - - function complete() { - self.result.status = self.status(); + function complete(enabledAgain) { + self.result.status = self.status(enabledAgain); self.resultCallback(self.result); if (onComplete) { @@ -324,16 +373,43 @@ getJasmineRequireObj().Spec = function(j$) { } }; + Spec.prototype.onException = function onException(e) { + if (Spec.isPendingSpecException(e)) { + this.pend(extractCustomPendingMessage(e)); + return; + } + + if (e instanceof j$.errors.ExpectationFailed) { + return; + } + + this.addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: e + }, true); + }; + Spec.prototype.disable = function() { this.disabled = true; }; - Spec.prototype.pend = function() { + Spec.prototype.pend = function(message) { this.markedPending = true; + if (message) { + this.result.pendingReason = message; + } }; - Spec.prototype.status = function() { - if (this.disabled) { + Spec.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; + + Spec.prototype.status = function(enabled) { + if (this.disabled || enabled === false) { return 'disabled'; } @@ -348,10 +424,22 @@ getJasmineRequireObj().Spec = function(j$) { } }; + Spec.prototype.isExecutable = function() { + return !this.disabled; + }; + Spec.prototype.getFullName = function() { return this.getSpecName(this); }; + var extractCustomPendingMessage = function(e) { + var fullMessage = e.toString(), + boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage), + boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length; + + return fullMessage.substr(boilerplateEnd); + }; + Spec.pendingSpecExceptionMessage = '=> marked Pending'; Spec.isPendingSpecException = function(e) { @@ -365,6 +453,53 @@ if (typeof window == void 0 && typeof exports == 'object') { exports.Spec = jasmineRequire.Spec; } +/*jshint bitwise: false*/ + +getJasmineRequireObj().Order = function() { + function Order(options) { + this.random = 'random' in options ? options.random : true; + var seed = this.seed = options.seed || generateSeed(); + this.sort = this.random ? randomOrder : naturalOrder; + + function naturalOrder(items) { + return items; + } + + function randomOrder(items) { + var copy = items.slice(); + copy.sort(function(a, b) { + return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id); + }); + return copy; + } + + function generateSeed() { + return String(Math.random()).slice(-5); + } + + // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function + // used to get a different output when the key changes slighly. + // We use your return to sort the children randomly in a consistent way when + // used in conjunction with a seed + + function jenkinsHash(key) { + var hash, i; + for(hash = i = 0; i < key.length; ++i) { + hash += key.charCodeAt(i); + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + + } + + return Order; +}; + getJasmineRequireObj().Env = function(j$) { function Env(options) { options = options || {}; @@ -378,14 +513,25 @@ getJasmineRequireObj().Env = function(j$) { var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; - this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); + this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global)); var runnableLookupTable = {}; - - var spies = []; + var runnableResources = {}; var currentSpec = null; - var currentSuite = null; + var currentlyExecutingSuites = []; + var currentDeclarationSuite = null; + var throwOnExpectationFailure = false; + var random = false; + var seed = null; + + var currentSuite = function() { + return currentlyExecutingSuites[currentlyExecutingSuites.length - 1]; + }; + + var currentRunnable = function() { + return currentSpec || currentSuite(); + }; var reporter = new j$.ReportDispatcher([ 'jasmineStarted', @@ -400,11 +546,21 @@ getJasmineRequireObj().Env = function(j$) { return true; }; - var equalityTesters = []; - - var customEqualityTesters = []; this.addCustomEqualityTester = function(tester) { - customEqualityTesters.push(tester); + if(!currentRunnable()) { + throw new Error('Custom Equalities must be added in a before function or a spec'); + } + runnableResources[currentRunnable().id].customEqualityTesters.push(tester); + }; + + this.addMatchers = function(matchersToAdd) { + if(!currentRunnable()) { + throw new Error('Matchers must be added in a before function or a spec'); + } + var customMatchers = runnableResources[currentRunnable().id].customMatchers; + for (var matcherName in matchersToAdd) { + customMatchers[matcherName] = matchersToAdd[matcherName]; + } }; j$.Expectation.addCoreMatchers(j$.matchers); @@ -422,7 +578,8 @@ getJasmineRequireObj().Env = function(j$) { var expectationFactory = function(actual, spec) { return j$.Expectation.Factory({ util: j$.matchersUtil, - customEqualityTesters: customEqualityTesters, + customEqualityTesters: runnableResources[spec.id].customEqualityTesters, + customMatchers: runnableResources[spec.id].customMatchers, actual: actual, addExpectationResult: addExpectationResult }); @@ -432,30 +589,38 @@ getJasmineRequireObj().Env = function(j$) { } }; - var specStarted = function(spec) { - currentSpec = spec; - reporter.specStarted(spec.result); + var defaultResourcesForRunnable = function(id, parentRunnableId) { + var resources = {spies: [], customEqualityTesters: [], customMatchers: {}}; + + if(runnableResources[parentRunnableId]){ + resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters); + resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers); + } + + runnableResources[id] = resources; }; - var beforeFns = function(suite) { - return function() { - var befores = []; - while(suite) { - befores = befores.concat(suite.beforeFns); - suite = suite.parentSuite; - } - return befores.reverse(); - }; + var clearResourcesForRunnable = function(id) { + spyRegistry.clearSpies(); + delete runnableResources[id]; }; - var afterFns = function(suite) { + var beforeAndAfterFns = function(suite) { return function() { - var afters = []; + var befores = [], + afters = []; + while(suite) { + befores = befores.concat(suite.beforeFns); afters = afters.concat(suite.afterFns); + suite = suite.parentSuite; } - return afters; + + return { + befores: befores.reverse(), + afters: afters + }; }; }; @@ -500,10 +665,34 @@ getJasmineRequireObj().Env = function(j$) { return j$.Spec.isPendingSpecException(e) || catchExceptions; }; + this.throwOnExpectationFailure = function(value) { + throwOnExpectationFailure = !!value; + }; + + this.throwingExpectationFailures = function() { + return throwOnExpectationFailure; + }; + + this.randomizeTests = function(value) { + random = !!value; + }; + + this.randomTests = function() { + return random; + }; + + this.seed = function(value) { + if (value) { + seed = value; + } + return seed; + }; + var queueRunnerFactory = function(options) { options.catchException = catchException; options.clearStack = options.clearStack || clearStack; - options.timer = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; + options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout}; + options.fail = self.fail; new j$.QueueRunner(options).execute(); }; @@ -512,66 +701,79 @@ getJasmineRequireObj().Env = function(j$) { env: this, id: getNextSuiteId(), description: 'Jasmine__TopLevel__Suite', - queueRunner: queueRunnerFactory, - resultCallback: function() {} // TODO - hook this up + queueRunner: queueRunnerFactory }); runnableLookupTable[topSuite.id] = topSuite; - currentSuite = topSuite; + defaultResourcesForRunnable(topSuite.id); + currentDeclarationSuite = topSuite; this.topSuite = function() { return topSuite; }; this.execute = function(runnablesToRun) { - runnablesToRun = runnablesToRun || [topSuite.id]; + if(!runnablesToRun) { + if (focusedRunnables.length) { + runnablesToRun = focusedRunnables; + } else { + runnablesToRun = [topSuite.id]; + } + } + + var order = new j$.Order({ + random: random, + seed: seed + }); - var allFns = []; - for(var i = 0; i < runnablesToRun.length; i++) { - var runnable = runnableLookupTable[runnablesToRun[i]]; - allFns.push((function(runnable) { return function(done) { runnable.execute(done); }; })(runnable)); + var processor = new j$.TreeProcessor({ + tree: topSuite, + runnableIds: runnablesToRun, + queueRunnerFactory: queueRunnerFactory, + nodeStart: function(suite) { + currentlyExecutingSuites.push(suite); + defaultResourcesForRunnable(suite.id, suite.parentSuite.id); + reporter.suiteStarted(suite.result); + }, + nodeComplete: function(suite, result) { + if (!suite.disabled) { + clearResourcesForRunnable(suite.id); + } + currentlyExecutingSuites.pop(); + reporter.suiteDone(result); + }, + orderChildren: function(node) { + return order.sort(node.children); + } + }); + + if(!processor.processTree().valid) { + throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times'); } reporter.jasmineStarted({ totalSpecsDefined: totalSpecsDefined }); - queueRunnerFactory({fns: allFns, onComplete: reporter.jasmineDone}); + processor.execute(function() { + reporter.jasmineDone({ + order: order + }); + }); }; this.addReporter = function(reporterToAdd) { reporter.addReporter(reporterToAdd); }; - this.addMatchers = function(matchersToAdd) { - j$.Expectation.addMatchers(matchersToAdd); - }; - - this.spyOn = function(obj, methodName) { - if (j$.util.isUndefined(obj)) { - throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); - } - - if (j$.util.isUndefined(obj[methodName])) { - throw new Error(methodName + '() method does not exist'); - } - - if (obj[methodName] && j$.isSpy(obj[methodName])) { - //TODO?: should this return the current spy? Downside: may cause user confusion about spy state - throw new Error(methodName + ' has already been spied upon'); + var spyRegistry = new j$.SpyRegistry({currentSpies: function() { + if(!currentRunnable()) { + throw new Error('Spies must be created in a before function or a spec'); } + return runnableResources[currentRunnable().id].spies; + }}); - var spy = j$.createSpy(methodName, obj[methodName]); - - spies.push({ - spy: spy, - baseObj: obj, - methodName: methodName, - originalValue: obj[methodName] - }); - - obj[methodName] = spy; - - return spy; + this.spyOn = function() { + return spyRegistry.spyOn.apply(spyRegistry, arguments); }; var suiteFactory = function(description) { @@ -579,12 +781,10 @@ getJasmineRequireObj().Env = function(j$) { env: self, id: getNextSuiteId(), description: description, - parentSuite: currentSuite, - queueRunner: queueRunnerFactory, - onStart: suiteStarted, - resultCallback: function(attrs) { - reporter.suiteDone(attrs); - } + parentSuite: currentDeclarationSuite, + expectationFactory: expectationFactory, + expectationResultFactory: expectationResultFactory, + throwOnExpectationFailure: throwOnExpectationFailure }); runnableLookupTable[suite.id] = suite; @@ -593,10 +793,40 @@ getJasmineRequireObj().Env = function(j$) { this.describe = function(description, specDefinitions) { var suite = suiteFactory(description); + if (specDefinitions.length > 0) { + throw new Error('describe does not expect a done parameter'); + } + if (currentDeclarationSuite.markedPending) { + suite.pend(); + } + addSpecsToSuite(suite, specDefinitions); + return suite; + }; - var parentSuite = currentSuite; + this.xdescribe = function(description, specDefinitions) { + var suite = suiteFactory(description); + suite.pend(); + addSpecsToSuite(suite, specDefinitions); + return suite; + }; + + var focusedRunnables = []; + + this.fdescribe = function(description, specDefinitions) { + var suite = suiteFactory(description); + suite.isFocused = true; + + focusedRunnables.push(suite.id); + unfocusAncestor(); + addSpecsToSuite(suite, specDefinitions); + + return suite; + }; + + function addSpecsToSuite(suite, specDefinitions) { + var parentSuite = currentDeclarationSuite; parentSuite.addChild(suite); - currentSuite = suite; + currentDeclarationSuite = suite; var declarationError = null; try { @@ -606,31 +836,43 @@ getJasmineRequireObj().Env = function(j$) { } if (declarationError) { - this.it('encountered a declaration exception', function() { + self.it('encountered a declaration exception', function() { throw declarationError; }); } - currentSuite = parentSuite; + currentDeclarationSuite = parentSuite; + } - return suite; - }; + function findFocusedAncestor(suite) { + while (suite) { + if (suite.isFocused) { + return suite.id; + } + suite = suite.parentSuite; + } - this.xdescribe = function(description, specDefinitions) { - var suite = this.describe(description, specDefinitions); - suite.disable(); - return suite; - }; + return null; + } - var specFactory = function(description, fn, suite) { - totalSpecsDefined++; + function unfocusAncestor() { + var focusedAncestor = findFocusedAncestor(currentDeclarationSuite); + if (focusedAncestor) { + for (var i = 0; i < focusedRunnables.length; i++) { + if (focusedRunnables[i] === focusedAncestor) { + focusedRunnables.splice(i, 1); + break; + } + } + } + } + var specFactory = function(description, fn, suite, timeout) { + totalSpecsDefined++; var spec = new j$.Spec({ id: getNextSpecId(), - beforeFns: beforeFns(suite), - afterFns: afterFns(suite), + beforeAndAfterFns: beforeAndAfterFns(suite), expectationFactory: expectationFactory, - exceptionFormatter: exceptionFormatter, resultCallback: specResultCallback, getSpecName: function(spec) { return getSpecName(spec, suite); @@ -639,7 +881,12 @@ getJasmineRequireObj().Env = function(j$) { description: description, expectationResultFactory: expectationResultFactory, queueRunnerFactory: queueRunnerFactory, - fn: fn + userContext: function() { return suite.clonedSharedUserContext(); }, + queueableFn: { + fn: fn, + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + }, + throwOnExpectationFailure: throwOnExpectationFailure }); runnableLookupTable[spec.id] = spec; @@ -650,57 +897,101 @@ getJasmineRequireObj().Env = function(j$) { return spec; - function removeAllSpies() { - for (var i = 0; i < spies.length; i++) { - var spyEntry = spies[i]; - spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; - } - spies = []; - } - function specResultCallback(result) { - removeAllSpies(); - j$.Expectation.resetMatchers(); - customEqualityTesters = []; + clearResourcesForRunnable(spec.id); currentSpec = null; reporter.specDone(result); } + + function specStarted(spec) { + currentSpec = spec; + defaultResourcesForRunnable(spec.id, suite.id); + reporter.specStarted(spec.result); + } }; - var suiteStarted = function(suite) { - reporter.suiteStarted(suite.result); + this.it = function(description, fn, timeout) { + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); + if (currentDeclarationSuite.markedPending) { + spec.pend(); + } + currentDeclarationSuite.addChild(spec); + return spec; }; - this.it = function(description, fn) { - var spec = specFactory(description, fn, currentSuite); - currentSuite.addChild(spec); + this.xit = function() { + var spec = this.it.apply(this, arguments); + spec.pend('Temporarily disabled with xit'); return spec; }; - this.xit = function(description, fn) { - var spec = this.it(description, fn); - spec.pend(); + this.fit = function(description, fn, timeout){ + var spec = specFactory(description, fn, currentDeclarationSuite, timeout); + currentDeclarationSuite.addChild(spec); + focusedRunnables.push(spec.id); + unfocusAncestor(); return spec; }; this.expect = function(actual) { - if (!currentSpec) { + if (!currentRunnable()) { throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out'); } - return currentSpec.expect(actual); + return currentRunnable().expect(actual); }; - this.beforeEach = function(beforeEachFunction) { - currentSuite.beforeEach(beforeEachFunction); + this.beforeEach = function(beforeEachFunction, timeout) { + currentDeclarationSuite.beforeEach({ + fn: beforeEachFunction, + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + }); }; - this.afterEach = function(afterEachFunction) { - currentSuite.afterEach(afterEachFunction); + this.beforeAll = function(beforeAllFunction, timeout) { + currentDeclarationSuite.beforeAll({ + fn: beforeAllFunction, + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + }); + }; + + this.afterEach = function(afterEachFunction, timeout) { + currentDeclarationSuite.afterEach({ + fn: afterEachFunction, + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + }); + }; + + this.afterAll = function(afterAllFunction, timeout) { + currentDeclarationSuite.afterAll({ + fn: afterAllFunction, + timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; } + }); + }; + + this.pending = function(message) { + var fullMessage = j$.Spec.pendingSpecExceptionMessage; + if(message) { + fullMessage += message; + } + throw fullMessage; }; - this.pending = function() { - throw j$.Spec.pendingSpecExceptionMessage; + this.fail = function(error) { + var message = 'Failed'; + if (error) { + message += ': '; + message += error.message || error; + } + + currentRunnable().addExpectationResult(false, { + matcherName: '', + passed: false, + expected: '', + actual: '', + message: message, + error: error && error.message ? error : null + }); }; } @@ -720,6 +1011,7 @@ getJasmineRequireObj().JsApiReporter = function() { this.started = false; this.finished = false; + this.runDetails = {}; this.jasmineStarted = function() { this.started = true; @@ -729,8 +1021,9 @@ getJasmineRequireObj().JsApiReporter = function() { var executionTime; - this.jasmineDone = function() { + this.jasmineDone = function(runDetails) { this.finished = true; + this.runDetails = runDetails; executionTime = timer.elapsed(); status = 'done'; }; @@ -739,26 +1032,31 @@ getJasmineRequireObj().JsApiReporter = function() { return status; }; - var suites = {}; + var suites = [], + suites_hash = {}; this.suiteStarted = function(result) { - storeSuite(result); + suites_hash[result.id] = result; }; this.suiteDone = function(result) { storeSuite(result); }; + this.suiteResults = function(index, length) { + return suites.slice(index, index + length); + }; + function storeSuite(result) { - suites[result.id] = result; + suites.push(result); + suites_hash[result.id] = result; } this.suites = function() { - return suites; + return suites_hash; }; var specs = []; - this.specStarted = function(result) { }; this.specDone = function(result) { specs.push(result); @@ -781,43 +1079,6 @@ getJasmineRequireObj().JsApiReporter = function() { return JsApiReporter; }; -getJasmineRequireObj().Any = function() { - - function Any(expectedObject) { - this.expectedObject = expectedObject; - } - - Any.prototype.jasmineMatches = function(other) { - if (this.expectedObject == String) { - return typeof other == 'string' || other instanceof String; - } - - if (this.expectedObject == Number) { - return typeof other == 'number' || other instanceof Number; - } - - if (this.expectedObject == Function) { - return typeof other == 'function' || other instanceof Function; - } - - if (this.expectedObject == Object) { - return typeof other == 'object'; - } - - if (this.expectedObject == Boolean) { - return typeof other == 'boolean'; - } - - return other instanceof this.expectedObject; - }; - - Any.prototype.jasmineToString = function() { - return '<jasmine.any(' + this.expectedObject + ')>'; - }; - - return Any; -}; - getJasmineRequireObj().CallTracker = function() { function CallTracker() { @@ -870,7 +1131,7 @@ getJasmineRequireObj().CallTracker = function() { }; getJasmineRequireObj().Clock = function() { - function Clock(global, delayedFunctionScheduler, mockDate) { + function Clock(global, delayedFunctionSchedulerFactory, mockDate) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, @@ -885,19 +1146,24 @@ getJasmineRequireObj().Clock = function() { clearInterval: clearInterval }, installed = false, + delayedFunctionScheduler, timer; self.install = function() { + if(!originalTimingFunctionsIntact()) { + throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?'); + } replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; + delayedFunctionScheduler = delayedFunctionSchedulerFactory(); installed = true; return self; }; self.uninstall = function() { - delayedFunctionScheduler.reset(); + delayedFunctionScheduler = null; mockDate.uninstall(); replace(global, realTimingFunctions); @@ -905,6 +1171,15 @@ getJasmineRequireObj().Clock = function() { installed = false; }; + self.withMock = function(closure) { + this.install(); + try { + closure(); + } finally { + this.uninstall(); + } + }; + self.mockDate = function(initialDate) { mockDate.install(initialDate); }; @@ -948,6 +1223,13 @@ getJasmineRequireObj().Clock = function() { return self; + function originalTimingFunctionsIntact() { + return global.setTimeout === realTimingFunctions.setTimeout && + global.clearTimeout === realTimingFunctions.clearTimeout && + global.setInterval === realTimingFunctions.setInterval && + global.clearInterval === realTimingFunctions.clearInterval; + } + function legacyIE() { //if these methods are polyfilled, apply will be present return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply; @@ -1057,13 +1339,6 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { } }; - self.reset = function() { - currentTime = 0; - scheduledLookup = []; - scheduledFunctions = {}; - delayedFnCount = 0; - }; - return self; function indexOfFirstToPass(array, testFn) { @@ -1099,6 +1374,12 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { scheduledFn.runAtMillis + scheduledFn.millis); } + function forEachFunction(funcsToRun, callback) { + for (var i = 0; i < funcsToRun.length; ++i) { + callback(funcsToRun[i]); + } + } + function runScheduledFunctions(endTime) { if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) { return; @@ -1110,14 +1391,15 @@ getJasmineRequireObj().DelayedFunctionScheduler = function() { var funcsToRun = scheduledFunctions[currentTime]; delete scheduledFunctions[currentTime]; - for (var i = 0; i < funcsToRun.length; ++i) { - var funcToRun = funcsToRun[i]; - funcToRun.funcToCall.apply(null, funcToRun.params || []); - + forEachFunction(funcsToRun, function(funcToRun) { if (funcToRun.recurring) { reschedule(funcToRun); } - } + }); + + forEachFunction(funcsToRun, function(funcToRun) { + funcToRun.funcToCall.apply(null, funcToRun.params || []); + }); } while (scheduledLookup.length > 0 && // checking first if we're out of time prevents setTimeout(0) // scheduled in a funcToRun from forcing an extra iteration @@ -1161,8 +1443,6 @@ getJasmineRequireObj().ExceptionFormatter = function() { getJasmineRequireObj().Expectation = function() { - var matchers = {}; - function Expectation(options) { this.util = options.util || { buildFailureMessage: function() {} }; this.customEqualityTesters = options.customEqualityTesters || []; @@ -1170,8 +1450,9 @@ getJasmineRequireObj().Expectation = function() { this.addExpectationResult = options.addExpectationResult || function(){}; this.isNot = options.isNot; - for (var matcherName in matchers) { - this[matcherName] = matchers[matcherName]; + var customMatchers = options.customMatchers || {}; + for (var matcherName in customMatchers) { + this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]); } } @@ -1238,19 +1519,6 @@ getJasmineRequireObj().Expectation = function() { } }; - Expectation.addMatchers = function(matchersToAdd) { - for (var name in matchersToAdd) { - var matcher = matchersToAdd[name]; - matchers[name] = Expectation.prototype.wrapCompare(name, matcher); - } - }; - - Expectation.resetMatchers = function() { - for (var name in matchers) { - delete matchers[name]; - } - }; - Expectation.Factory = function(options) { options = options || {}; @@ -1273,15 +1541,20 @@ getJasmineRequireObj().buildExpectationResult = function() { var messageFormatter = options.messageFormatter || function() {}, stackFormatter = options.stackFormatter || function() {}; - return { + var result = { matcherName: options.matcherName, - expected: options.expected, - actual: options.actual, message: message(), stack: stack(), passed: options.passed }; + if(!result.passed) { + result.expected = options.expected; + result.actual = options.actual; + } + + return result; + function message() { if (options.passed) { return 'Passed.'; @@ -1352,15 +1625,31 @@ getJasmineRequireObj().MockDate = function() { return self; function FakeDate() { - if (arguments.length === 0) { - return new GlobalDate(currentTime); - } else { - return new GlobalDate(arguments[0], arguments[1], arguments[2], - arguments[3], arguments[4], arguments[5], arguments[6]); + switch(arguments.length) { + case 0: + return new GlobalDate(currentTime); + case 1: + return new GlobalDate(arguments[0]); + case 2: + return new GlobalDate(arguments[0], arguments[1]); + case 3: + return new GlobalDate(arguments[0], arguments[1], arguments[2]); + case 4: + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]); + case 5: + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], + arguments[4]); + case 6: + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], + arguments[4], arguments[5]); + default: + return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3], + arguments[4], arguments[5], arguments[6]); } } function createDateProperties() { + FakeDate.prototype = GlobalDate.prototype; FakeDate.now = function() { if (GlobalDate.now) { @@ -1380,41 +1669,6 @@ getJasmineRequireObj().MockDate = function() { return MockDate; }; -getJasmineRequireObj().ObjectContaining = function(j$) { - - function ObjectContaining(sample) { - this.sample = sample; - } - - ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { - if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } - - mismatchKeys = mismatchKeys || []; - mismatchValues = mismatchValues || []; - - var hasKey = function(obj, keyName) { - return obj !== null && !j$.util.isUndefined(obj[keyName]); - }; - - for (var property in this.sample) { - if (!hasKey(other, property) && hasKey(this.sample, property)) { - mismatchKeys.push('expected has key \'' + property + '\', but missing from actual.'); - } - else if (!j$.matchersUtil.equals(other[property], this.sample[property])) { - mismatchValues.push('\'' + property + '\' was \'' + (other[property] ? j$.util.htmlEscape(other[property].toString()) : other[property]) + '\' in actual, but was \'' + (this.sample[property] ? j$.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + '\' in expected.'); - } - } - - return (mismatchKeys.length === 0 && mismatchValues.length === 0); - }; - - ObjectContaining.prototype.jasmineToString = function() { - return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>'; - }; - - return ObjectContaining; -}; - getJasmineRequireObj().pp = function(j$) { function PrettyPrinter() { @@ -1447,6 +1701,8 @@ getJasmineRequireObj().pp = function(j$) { this.emitScalar('HTMLNode'); } else if (value instanceof Date) { this.emitScalar('Date(' + value + ')'); + } else if (value.toString && typeof value === 'object' && !(value instanceof Array) && value.toString !== Object.prototype.toString) { + this.emitScalar(value.toString()); } else if (j$.util.arrayContains(this.seen, value)) { this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>'); } else if (j$.isArray_(value) || j$.isA_('Object', value)) { @@ -1510,17 +1766,36 @@ getJasmineRequireObj().pp = function(j$) { if(array.length > length){ this.append(', ...'); } + + var self = this; + var first = array.length === 0; + this.iterateObject(array, function(property, isGetter) { + if (property.match(/^\d+$/)) { + return; + } + + if (first) { + first = false; + } else { + self.append(', '); + } + + self.formatProperty(array, property, isGetter); + }); + this.append(' ]'); }; StringPrettyPrinter.prototype.emitObject = function(obj) { + var constructorName = obj.constructor ? j$.fnNameFor(obj.constructor) : 'null'; + this.append(constructorName); + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Object'); return; } var self = this; - this.append('{ '); + this.append('({ '); var first = true; this.iterateObject(obj, function(property, isGetter) { @@ -1530,16 +1805,20 @@ getJasmineRequireObj().pp = function(j$) { self.append(', '); } - self.append(property); - self.append(': '); + self.formatProperty(obj, property, isGetter); + }); + + this.append(' })'); + }; + + StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) { + this.append(property); + this.append(': '); if (isGetter) { - self.append('<getter>'); + this.append('<getter>'); } else { - self.format(obj[property]); + this.format(obj[property]); } - }); - - this.append(' }'); }; StringPrettyPrinter.prototype.append = function(value) { @@ -1566,31 +1845,33 @@ getJasmineRequireObj().QueueRunner = function(j$) { } function QueueRunner(attrs) { - this.fns = attrs.fns || []; + this.queueableFns = attrs.queueableFns || []; this.onComplete = attrs.onComplete || function() {}; this.clearStack = attrs.clearStack || function(fn) {fn();}; this.onException = attrs.onException || function() {}; this.catchException = attrs.catchException || function() { return true; }; - this.enforceTimeout = attrs.enforceTimeout || function() { return false; }; - this.userContext = {}; - this.timer = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + this.userContext = attrs.userContext || {}; + this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout}; + this.fail = attrs.fail || function() {}; } QueueRunner.prototype.execute = function() { - this.run(this.fns, 0); + this.run(this.queueableFns, 0); }; - QueueRunner.prototype.run = function(fns, recursiveIndex) { - var length = fns.length, - self = this, - iterativeIndex; + QueueRunner.prototype.run = function(queueableFns, recursiveIndex) { + var length = queueableFns.length, + self = this, + iterativeIndex; + for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) { - var fn = fns[iterativeIndex]; - if (fn.length > 0) { - return attemptAsync(fn); + var queueableFn = queueableFns[iterativeIndex]; + if (queueableFn.fn.length > 0) { + attemptAsync(queueableFn); + return; } else { - attemptSync(fn); + attemptSync(queueableFn); } } @@ -1600,41 +1881,51 @@ getJasmineRequireObj().QueueRunner = function(j$) { this.clearStack(this.onComplete); } - function attemptSync(fn) { + function attemptSync(queueableFn) { try { - fn.call(self.userContext); + queueableFn.fn.call(self.userContext); } catch (e) { - handleException(e); + handleException(e, queueableFn); } } - function attemptAsync(fn) { + function attemptAsync(queueableFn) { var clearTimeout = function () { - Function.prototype.apply.apply(self.timer.clearTimeout, [j$.getGlobal(), [timeoutId]]); + Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]); }, next = once(function () { clearTimeout(timeoutId); - self.run(fns, iterativeIndex + 1); + self.run(queueableFns, iterativeIndex + 1); }), timeoutId; - if (self.enforceTimeout()) { - timeoutId = Function.prototype.apply.apply(self.timer.setTimeout, [j$.getGlobal(), [function() { - self.onException(new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.')); + next.fail = function() { + self.fail.apply(null, arguments); + next(); + }; + + if (queueableFn.timeout) { + timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() { + var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.'); + onException(error); next(); - }, j$.DEFAULT_TIMEOUT_INTERVAL]]); + }, queueableFn.timeout()]]); } try { - fn.call(self.userContext, next); + queueableFn.fn.call(self.userContext, next); } catch (e) { - handleException(e); + handleException(e, queueableFn); next(); } } - function handleException(e) { + function onException(e) { self.onException(e); + } + + function handleException(e, queueableFn) { + onException(e); if (!self.catchException(e)) { //TODO: set a var when we catch an exception and //use a finally block to close the loop in a nice way.. @@ -1682,6 +1973,67 @@ getJasmineRequireObj().ReportDispatcher = function() { }; +getJasmineRequireObj().SpyRegistry = function(j$) { + + function SpyRegistry(options) { + options = options || {}; + var currentSpies = options.currentSpies || function() { return []; }; + + this.spyOn = function(obj, methodName) { + if (j$.util.isUndefined(obj)) { + throw new Error('spyOn could not find an object to spy upon for ' + methodName + '()'); + } + + if (j$.util.isUndefined(methodName)) { + throw new Error('No method name supplied'); + } + + if (j$.util.isUndefined(obj[methodName])) { + throw new Error(methodName + '() method does not exist'); + } + + if (obj[methodName] && j$.isSpy(obj[methodName])) { + //TODO?: should this return the current spy? Downside: may cause user confusion about spy state + throw new Error(methodName + ' has already been spied upon'); + } + + var descriptor; + try { + descriptor = Object.getOwnPropertyDescriptor(obj, methodName); + } catch(e) { + // IE 8 doesn't support `definePropery` on non-DOM nodes + } + + if (descriptor && !(descriptor.writable || descriptor.set)) { + throw new Error(methodName + ' is not declared writable or has no setter'); + } + + var spy = j$.createSpy(methodName, obj[methodName]); + + currentSpies().push({ + spy: spy, + baseObj: obj, + methodName: methodName, + originalValue: obj[methodName] + }); + + obj[methodName] = spy; + + return spy; + }; + + this.clearSpies = function() { + var spies = currentSpies(); + for (var i = 0; i < spies.length; i++) { + var spyEntry = spies[i]; + spyEntry.baseObj[spyEntry.methodName] = spyEntry.originalValue; + } + }; + } + + return SpyRegistry; +}; + getJasmineRequireObj().SpyStrategy = function() { function SpyStrategy(options) { @@ -1712,6 +2064,14 @@ getJasmineRequireObj().SpyStrategy = function() { return getSpy(); }; + this.returnValues = function() { + var values = Array.prototype.slice.call(arguments); + plan = function () { + return values.shift(); + }; + return getSpy(); + }; + this.throwError = function(something) { var error = (something instanceof Error) ? something : new Error(something); plan = function() { @@ -1734,31 +2094,36 @@ getJasmineRequireObj().SpyStrategy = function() { return SpyStrategy; }; -getJasmineRequireObj().Suite = function() { +getJasmineRequireObj().Suite = function(j$) { function Suite(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; this.description = attrs.description; - this.onStart = attrs.onStart || function() {}; - this.resultCallback = attrs.resultCallback || function() {}; - this.clearStack = attrs.clearStack || function(fn) {fn();}; + this.expectationFactory = attrs.expectationFactory; + this.expectationResultFactory = attrs.expectationResultFactory; + this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure; this.beforeFns = []; this.afterFns = []; - this.queueRunner = attrs.queueRunner || function() {}; + this.beforeAllFns = []; + this.afterAllFns = []; this.disabled = false; this.children = []; this.result = { id: this.id, - status: this.disabled ? 'disabled' : '', description: this.description, - fullName: this.getFullName() + fullName: this.getFullName(), + failedExpectations: [] }; } + Suite.prototype.expect = function(actual) { + return this.expectationFactory(actual, this); + }; + Suite.prototype.getFullName = function() { var fullName = this.description; for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { @@ -1773,51 +2138,131 @@ getJasmineRequireObj().Suite = function() { this.disabled = true; }; + Suite.prototype.pend = function(message) { + this.markedPending = true; + }; + Suite.prototype.beforeEach = function(fn) { this.beforeFns.unshift(fn); }; + Suite.prototype.beforeAll = function(fn) { + this.beforeAllFns.push(fn); + }; + Suite.prototype.afterEach = function(fn) { this.afterFns.unshift(fn); }; + Suite.prototype.afterAll = function(fn) { + this.afterAllFns.push(fn); + }; + Suite.prototype.addChild = function(child) { this.children.push(child); }; - Suite.prototype.execute = function(onComplete) { - var self = this; + Suite.prototype.status = function() { if (this.disabled) { - complete(); - return; + return 'disabled'; } - var allFns = []; + if (this.markedPending) { + return 'pending'; + } - for (var i = 0; i < this.children.length; i++) { - allFns.push(wrapChildAsAsync(this.children[i])); + if (this.result.failedExpectations.length > 0) { + return 'failed'; + } else { + return 'finished'; } + }; - this.onStart(this); + Suite.prototype.isExecutable = function() { + return !this.disabled; + }; - this.queueRunner({ - fns: allFns, - onComplete: complete - }); + Suite.prototype.canBeReentered = function() { + return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0; + }; - function complete() { - self.resultCallback(self.result); + Suite.prototype.getResult = function() { + this.result.status = this.status(); + return this.result; + }; - if (onComplete) { - onComplete(); + Suite.prototype.sharedUserContext = function() { + if (!this.sharedContext) { + this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {}; + } + + return this.sharedContext; + }; + + Suite.prototype.clonedSharedUserContext = function() { + return clone(this.sharedUserContext()); + }; + + Suite.prototype.onException = function() { + if (arguments[0] instanceof j$.errors.ExpectationFailed) { + return; + } + + if(isAfterAll(this.children)) { + var data = { + matcherName: '', + passed: false, + expected: '', + actual: '', + error: arguments[0] + }; + this.result.failedExpectations.push(this.expectationResultFactory(data)); + } else { + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + child.onException.apply(child, arguments); } } + }; - function wrapChildAsAsync(child) { - return function(done) { child.execute(done); }; + Suite.prototype.addExpectationResult = function () { + if(isAfterAll(this.children) && isFailure(arguments)){ + var data = arguments[1]; + this.result.failedExpectations.push(this.expectationResultFactory(data)); + if(this.throwOnExpectationFailure) { + throw new j$.errors.ExpectationFailed(); + } + } else { + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + try { + child.addExpectationResult.apply(child, arguments); + } catch(e) { + // keep going + } + } } }; + function isAfterAll(children) { + return children && children[0].result.status; + } + + function isFailure(args) { + return !args[0]; + } + + function clone(obj) { + var clonedObj = {}; + for (var prop in obj) { + if (obj.hasOwnProperty(prop)) { + clonedObj[prop] = obj[prop]; + } + } + + return clonedObj; + } + return Suite; }; @@ -1848,6 +2293,378 @@ getJasmineRequireObj().Timer = function() { return Timer; }; +getJasmineRequireObj().TreeProcessor = function() { + function TreeProcessor(attrs) { + var tree = attrs.tree, + runnableIds = attrs.runnableIds, + queueRunnerFactory = attrs.queueRunnerFactory, + nodeStart = attrs.nodeStart || function() {}, + nodeComplete = attrs.nodeComplete || function() {}, + orderChildren = attrs.orderChildren || function(node) { return node.children; }, + stats = { valid: true }, + processed = false, + defaultMin = Infinity, + defaultMax = 1 - Infinity; + + this.processTree = function() { + processNode(tree, false); + processed = true; + return stats; + }; + + this.execute = function(done) { + if (!processed) { + this.processTree(); + } + + if (!stats.valid) { + throw 'invalid order'; + } + + var childFns = wrapChildren(tree, 0); + + queueRunnerFactory({ + queueableFns: childFns, + userContext: tree.sharedUserContext(), + onException: function() { + tree.onException.apply(tree, arguments); + }, + onComplete: done + }); + }; + + function runnableIndex(id) { + for (var i = 0; i < runnableIds.length; i++) { + if (runnableIds[i] === id) { + return i; + } + } + } + + function processNode(node, parentEnabled) { + var executableIndex = runnableIndex(node.id); + + if (executableIndex !== undefined) { + parentEnabled = true; + } + + parentEnabled = parentEnabled && node.isExecutable(); + + if (!node.children) { + stats[node.id] = { + executable: parentEnabled && node.isExecutable(), + segments: [{ + index: 0, + owner: node, + nodes: [node], + min: startingMin(executableIndex), + max: startingMax(executableIndex) + }] + }; + } else { + var hasExecutableChild = false; + + var orderedChildren = orderChildren(node); + + for (var i = 0; i < orderedChildren.length; i++) { + var child = orderedChildren[i]; + + processNode(child, parentEnabled); + + if (!stats.valid) { + return; + } + + var childStats = stats[child.id]; + + hasExecutableChild = hasExecutableChild || childStats.executable; + } + + stats[node.id] = { + executable: hasExecutableChild + }; + + segmentChildren(node, orderedChildren, stats[node.id], executableIndex); + + if (!node.canBeReentered() && stats[node.id].segments.length > 1) { + stats = { valid: false }; + } + } + } + + function startingMin(executableIndex) { + return executableIndex === undefined ? defaultMin : executableIndex; + } + + function startingMax(executableIndex) { + return executableIndex === undefined ? defaultMax : executableIndex; + } + + function segmentChildren(node, orderedChildren, nodeStats, executableIndex) { + var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) }, + result = [currentSegment], + lastMax = defaultMax, + orderedChildSegments = orderChildSegments(orderedChildren); + + function isSegmentBoundary(minIndex) { + return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1; + } + + for (var i = 0; i < orderedChildSegments.length; i++) { + var childSegment = orderedChildSegments[i], + maxIndex = childSegment.max, + minIndex = childSegment.min; + + if (isSegmentBoundary(minIndex)) { + currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax}; + result.push(currentSegment); + } + + currentSegment.nodes.push(childSegment); + currentSegment.min = Math.min(currentSegment.min, minIndex); + currentSegment.max = Math.max(currentSegment.max, maxIndex); + lastMax = maxIndex; + } + + nodeStats.segments = result; + } + + function orderChildSegments(children) { + var specifiedOrder = [], + unspecifiedOrder = []; + + for (var i = 0; i < children.length; i++) { + var child = children[i], + segments = stats[child.id].segments; + + for (var j = 0; j < segments.length; j++) { + var seg = segments[j]; + + if (seg.min === defaultMin) { + unspecifiedOrder.push(seg); + } else { + specifiedOrder.push(seg); + } + } + } + + specifiedOrder.sort(function(a, b) { + return a.min - b.min; + }); + + return specifiedOrder.concat(unspecifiedOrder); + } + + function executeNode(node, segmentNumber) { + if (node.children) { + return { + fn: function(done) { + nodeStart(node); + + queueRunnerFactory({ + onComplete: function() { + nodeComplete(node, node.getResult()); + done(); + }, + queueableFns: wrapChildren(node, segmentNumber), + userContext: node.sharedUserContext(), + onException: function() { + node.onException.apply(node, arguments); + } + }); + } + }; + } else { + return { + fn: function(done) { node.execute(done, stats[node.id].executable); } + }; + } + } + + function wrapChildren(node, segmentNumber) { + var result = [], + segmentChildren = stats[node.id].segments[segmentNumber].nodes; + + for (var i = 0; i < segmentChildren.length; i++) { + result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index)); + } + + if (!stats[node.id].executable) { + return result; + } + + return node.beforeAllFns.concat(result).concat(node.afterAllFns); + } + } + + return TreeProcessor; +}; + +getJasmineRequireObj().Any = function(j$) { + + function Any(expectedObject) { + if (typeof expectedObject === 'undefined') { + throw new TypeError( + 'jasmine.any() expects to be passed a constructor function. ' + + 'Please pass one or use jasmine.anything() to match any object.' + ); + } + this.expectedObject = expectedObject; + } + + Any.prototype.asymmetricMatch = function(other) { + if (this.expectedObject == String) { + return typeof other == 'string' || other instanceof String; + } + + if (this.expectedObject == Number) { + return typeof other == 'number' || other instanceof Number; + } + + if (this.expectedObject == Function) { + return typeof other == 'function' || other instanceof Function; + } + + if (this.expectedObject == Object) { + return typeof other == 'object'; + } + + if (this.expectedObject == Boolean) { + return typeof other == 'boolean'; + } + + return other instanceof this.expectedObject; + }; + + Any.prototype.jasmineToString = function() { + return '<jasmine.any(' + j$.fnNameFor(this.expectedObject) + ')>'; + }; + + return Any; +}; + +getJasmineRequireObj().Anything = function(j$) { + + function Anything() {} + + Anything.prototype.asymmetricMatch = function(other) { + return !j$.util.isUndefined(other) && other !== null; + }; + + Anything.prototype.jasmineToString = function() { + return '<jasmine.anything>'; + }; + + return Anything; +}; + +getJasmineRequireObj().ArrayContaining = function(j$) { + function ArrayContaining(sample) { + this.sample = sample; + } + + ArrayContaining.prototype.asymmetricMatch = function(other) { + var className = Object.prototype.toString.call(this.sample); + if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); } + + for (var i = 0; i < this.sample.length; i++) { + var item = this.sample[i]; + if (!j$.matchersUtil.contains(other, item)) { + return false; + } + } + + return true; + }; + + ArrayContaining.prototype.jasmineToString = function () { + return '<jasmine.arrayContaining(' + jasmine.pp(this.sample) +')>'; + }; + + return ArrayContaining; +}; + +getJasmineRequireObj().ObjectContaining = function(j$) { + + function ObjectContaining(sample) { + this.sample = sample; + } + + function getPrototype(obj) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(obj); + } + + if (obj.constructor.prototype == obj) { + return null; + } + + return obj.constructor.prototype; + } + + function hasProperty(obj, property) { + if (!obj) { + return false; + } + + if (Object.prototype.hasOwnProperty.call(obj, property)) { + return true; + } + + return hasProperty(getPrototype(obj), property); + } + + ObjectContaining.prototype.asymmetricMatch = function(other) { + if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); } + + for (var property in this.sample) { + if (!hasProperty(other, property) || + !j$.matchersUtil.equals(this.sample[property], other[property])) { + return false; + } + } + + return true; + }; + + ObjectContaining.prototype.jasmineToString = function() { + return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>'; + }; + + return ObjectContaining; +}; + +getJasmineRequireObj().StringMatching = function(j$) { + + function StringMatching(expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error('Expected is not a String or a RegExp'); + } + + this.regexp = new RegExp(expected); + } + + StringMatching.prototype.asymmetricMatch = function(other) { + return this.regexp.test(other); + }; + + StringMatching.prototype.jasmineToString = function() { + return '<jasmine.stringMatching(' + this.regexp + ')>'; + }; + + return StringMatching; +}; + +getJasmineRequireObj().errors = function() { + function ExpectationFailed() {} + + ExpectationFailed.prototype = new Error(); + ExpectationFailed.prototype.constructor = ExpectationFailed; + + return { + ExpectationFailed: ExpectationFailed + }; +}; getJasmineRequireObj().matchersUtil = function(j$) { // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter? @@ -1861,7 +2678,9 @@ getJasmineRequireObj().matchersUtil = function(j$) { contains: function(haystack, needle, customTesters) { customTesters = customTesters || []; - if (Object.prototype.toString.apply(haystack) === '[object Array]') { + if ((Object.prototype.toString.apply(haystack) === '[object Array]') || + (!!haystack && !haystack.indexOf)) + { for (var i = 0; i < haystack.length; i++) { if (eq(haystack[i], needle, [], [], customTesters)) { return true; @@ -1869,6 +2688,7 @@ getJasmineRequireObj().matchersUtil = function(j$) { } return false; } + return !!haystack && haystack.indexOf(needle) >= 0; }, @@ -1898,11 +2718,37 @@ getJasmineRequireObj().matchersUtil = function(j$) { } }; + function isAsymmetric(obj) { + return obj && j$.isA_('Function', obj.asymmetricMatch); + } + + function asymmetricMatch(a, b) { + var asymmetricA = isAsymmetric(a), + asymmetricB = isAsymmetric(b); + + if (asymmetricA && asymmetricB) { + return undefined; + } + + if (asymmetricA) { + return a.asymmetricMatch(b); + } + + if (asymmetricB) { + return b.asymmetricMatch(a); + } + } + // Equality function lovingly adapted from isEqual in // [Underscore](http://underscorejs.org) function eq(a, b, aStack, bStack, customTesters) { var result = true; + var asymmetricResult = asymmetricMatch(a, b); + if (!j$.util.isUndefined(asymmetricResult)) { + return asymmetricResult; + } + for (var i = 0; i < customTesters.length; i++) { var customTesterResult = customTesters[i](a, b); if (!j$.util.isUndefined(customTesterResult)) { @@ -1910,27 +2756,6 @@ getJasmineRequireObj().matchersUtil = function(j$) { } } - if (a instanceof j$.Any) { - result = a.jasmineMatches(b); - if (result) { - return true; - } - } - - if (b instanceof j$.Any) { - result = b.jasmineMatches(a); - if (result) { - return true; - } - } - - if (b instanceof j$.ObjectContaining) { - result = b.jasmineMatches(a); - if (result) { - return true; - } - } - if (a instanceof Error && b instanceof Error) { return a.message == b.message; } @@ -1966,6 +2791,29 @@ getJasmineRequireObj().matchersUtil = function(j$) { a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') { return false; } + + var aIsDomNode = j$.isDomNode(a); + var bIsDomNode = j$.isDomNode(b); + if (aIsDomNode && bIsDomNode) { + // At first try to use DOM3 method isEqualNode + if (a.isEqualNode) { + return a.isEqualNode(b); + } + // IE8 doesn't support isEqualNode, try to use outerHTML && innerText + var aIsElement = a instanceof Element; + var bIsElement = b instanceof Element; + if (aIsElement && bIsElement) { + return a.outerHTML == b.outerHTML; + } + if (aIsElement || bIsElement) { + return false; + } + return a.innerText == b.innerText && a.textContent == b.textContent; + } + if (aIsDomNode || bIsDomNode) { + return false; + } + // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; @@ -1979,23 +2827,20 @@ getJasmineRequireObj().matchersUtil = function(j$) { bStack.push(b); var size = 0; // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack, customTesters))) { break; } - } - } - } else { + // Compare array lengths to determine if a deep comparison is necessary. + if (className == '[object Array]' && a.length !== b.length) { + result = false; + } + + if (result) { // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(isFunction(aCtor) && (aCtor instanceof aCtor) && - isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; + // or `Array`s from different frames are. + if (className !== '[object Array]') { + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && + isFunction(bCtor) && bCtor instanceof bCtor)) { + return false; + } } // Deep compare objects. for (var key in a) { @@ -2021,7 +2866,7 @@ getJasmineRequireObj().matchersUtil = function(j$) { return result; function has(obj, key) { - return obj.hasOwnProperty(key); + return Object.prototype.hasOwnProperty.call(obj, key); } function isFunction(obj) { @@ -2256,6 +3101,37 @@ getJasmineRequireObj().toHaveBeenCalled = function(j$) { return toHaveBeenCalled; }; +getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) { + + function toHaveBeenCalledTimes() { + return { + compare: function(actual, expected) { + if (!j$.isSpy(actual)) { + throw new Error('Expected a spy, but got ' + j$.pp(actual) + '.'); + } + + var args = Array.prototype.slice.call(arguments, 0), + result = { pass: false }; + + if(!expected){ + throw new Error('Expected times failed is required as an argument.'); + } + + actual = args[0]; + var calls = actual.calls.count(); + var timesMessage = expected === 1 ? 'once' : expected + ' times'; + result.pass = calls === expected; + result.message = result.pass ? + 'Expected spy ' + actual.and.identity() + ' not to have been called ' + timesMessage + '. It was called ' + calls + ' times.' : + 'Expected spy ' + actual.and.identity() + ' to have been called ' + timesMessage + '. It was called ' + calls + ' times.'; + return result; + } + }; + } + + return toHaveBeenCalledTimes; +}; + getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { function toHaveBeenCalledWith(util, customEqualityTesters) { @@ -2290,11 +3166,15 @@ getJasmineRequireObj().toHaveBeenCalledWith = function(j$) { return toHaveBeenCalledWith; }; -getJasmineRequireObj().toMatch = function() { +getJasmineRequireObj().toMatch = function(j$) { function toMatch() { return { compare: function(actual, expected) { + if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) { + throw new Error('Expected is not a String or a RegExp'); + } + var regexp = new RegExp(expected); return { @@ -2355,24 +3235,19 @@ getJasmineRequireObj().toThrow = function(j$) { }; getJasmineRequireObj().toThrowError = function(j$) { - function toThrowError (util) { + function toThrowError () { return { compare: function(actual) { var threw = false, pass = {pass: true}, fail = {pass: false}, - thrown, - errorType, - message, - regexp, - name, - constructorName; + thrown; if (typeof actual != 'function') { throw new Error('Actual is not a Function'); } - extractExpectedParams.apply(null, arguments); + var errorMatcher = getMatcher.apply(null, arguments); try { actual(); @@ -2391,126 +3266,189 @@ getJasmineRequireObj().toThrowError = function(j$) { return fail; } - if (arguments.length == 1) { - pass.message = 'Expected function not to throw an Error, but it threw ' + fnNameFor(thrown) + '.'; + if (errorMatcher.hasNoSpecifics()) { + pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.'; return pass; } - if (errorType) { - name = fnNameFor(errorType); - constructorName = fnNameFor(thrown.constructor); + if (errorMatcher.matches(thrown)) { + pass.message = function() { + return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.'; + }; + return pass; + } else { + fail.message = function() { + return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + + ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.'; + }; + return fail; } + } + }; - if (errorType && message) { - if (thrown.constructor == errorType && util.equals(thrown.message, message)) { - pass.message = function() { return 'Expected function not to throw ' + name + ' with message ' + j$.pp(message) + '.'; }; - return pass; - } else { - fail.message = function() { return 'Expected function to throw ' + name + ' with message ' + j$.pp(message) + - ', but it threw ' + constructorName + ' with message ' + j$.pp(thrown.message) + '.'; }; - return fail; - } - } + function getMatcher() { + var expected = null, + errorType = null; - if (errorType && regexp) { - if (thrown.constructor == errorType && regexp.test(thrown.message)) { - pass.message = function() { return 'Expected function not to throw ' + name + ' with message matching ' + j$.pp(regexp) + '.'; }; - return pass; - } else { - fail.message = function() { return 'Expected function to throw ' + name + ' with message matching ' + j$.pp(regexp) + - ', but it threw ' + constructorName + ' with message ' + j$.pp(thrown.message) + '.'; }; - return fail; - } + if (arguments.length == 2) { + expected = arguments[1]; + if (isAnErrorType(expected)) { + errorType = expected; + expected = null; } - - if (errorType) { - if (thrown.constructor == errorType) { - pass.message = 'Expected function not to throw ' + name + '.'; - return pass; - } else { - fail.message = 'Expected function to throw ' + name + ', but it threw ' + constructorName + '.'; - return fail; - } + } else if (arguments.length > 2) { + errorType = arguments[1]; + expected = arguments[2]; + if (!isAnErrorType(errorType)) { + throw new Error('Expected error type is not an Error.'); } + } - if (message) { - if (thrown.message == message) { - pass.message = function() { return 'Expected function not to throw an exception with message ' + j$.pp(message) + '.'; }; - return pass; - } else { - fail.message = function() { return 'Expected function to throw an exception with message ' + j$.pp(message) + - ', but it threw an exception with message ' + j$.pp(thrown.message) + '.'; }; - return fail; - } + if (expected && !isStringOrRegExp(expected)) { + if (errorType) { + throw new Error('Expected error message is not a string or RegExp.'); + } else { + throw new Error('Expected is not an Error, string, or RegExp.'); } + } - if (regexp) { - if (regexp.test(thrown.message)) { - pass.message = function() { return 'Expected function not to throw an exception with a message matching ' + j$.pp(regexp) + '.'; }; - return pass; - } else { - fail.message = function() { return 'Expected function to throw an exception with a message matching ' + j$.pp(regexp) + - ', but it threw an exception with message ' + j$.pp(thrown.message) + '.'; }; - return fail; - } + function messageMatch(message) { + if (typeof expected == 'string') { + return expected == message; + } else { + return expected.test(message); } + } - function fnNameFor(func) { - return func.name || func.toString().match(/^\s*function\s*(\w*)\s*\(/)[1]; - } + return { + errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception', + thrownDescription: function(thrown) { + var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception', + thrownMessage = ''; - function extractExpectedParams() { - if (arguments.length == 1) { - return; + if (expected) { + thrownMessage = ' with message ' + j$.pp(thrown.message); } - if (arguments.length == 2) { - var expected = arguments[1]; - - if (expected instanceof RegExp) { - regexp = expected; - } else if (typeof expected == 'string') { - message = expected; - } else if (checkForAnErrorType(expected)) { - errorType = expected; - } - - if (!(errorType || message || regexp)) { - throw new Error('Expected is not an Error, string, or RegExp.'); - } + return thrownName + thrownMessage; + }, + messageDescription: function() { + if (expected === null) { + return ''; + } else if (expected instanceof RegExp) { + return ' with a message matching ' + j$.pp(expected); } else { - if (checkForAnErrorType(arguments[1])) { - errorType = arguments[1]; - } else { - throw new Error('Expected error type is not an Error.'); - } - - if (arguments[2] instanceof RegExp) { - regexp = arguments[2]; - } else if (typeof arguments[2] == 'string') { - message = arguments[2]; - } else { - throw new Error('Expected error message is not a string or RegExp.'); - } + return ' with message ' + j$.pp(expected); } + }, + hasNoSpecifics: function() { + return expected === null && errorType === null; + }, + matches: function(error) { + return (errorType === null || error instanceof errorType) && + (expected === null || messageMatch(error.message)); } + }; + } - function checkForAnErrorType(type) { - if (typeof type !== 'function') { - return false; - } + function isStringOrRegExp(potential) { + return potential instanceof RegExp || (typeof potential == 'string'); + } - var Surrogate = function() {}; - Surrogate.prototype = type.prototype; - return (new Surrogate()) instanceof Error; - } + function isAnErrorType(type) { + if (typeof type !== 'function') { + return false; } - }; + + var Surrogate = function() {}; + Surrogate.prototype = type.prototype; + return (new Surrogate()) instanceof Error; + } } return toThrowError; }; +getJasmineRequireObj().interface = function(jasmine, env) { + var jasmineInterface = { + describe: function(description, specDefinitions) { + return env.describe(description, specDefinitions); + }, + + xdescribe: function(description, specDefinitions) { + return env.xdescribe(description, specDefinitions); + }, + + fdescribe: function(description, specDefinitions) { + return env.fdescribe(description, specDefinitions); + }, + + it: function() { + return env.it.apply(env, arguments); + }, + + xit: function() { + return env.xit.apply(env, arguments); + }, + + fit: function() { + return env.fit.apply(env, arguments); + }, + + beforeEach: function() { + return env.beforeEach.apply(env, arguments); + }, + + afterEach: function() { + return env.afterEach.apply(env, arguments); + }, + + beforeAll: function() { + return env.beforeAll.apply(env, arguments); + }, + + afterAll: function() { + return env.afterAll.apply(env, arguments); + }, + + expect: function(actual) { + return env.expect(actual); + }, + + pending: function() { + return env.pending.apply(env, arguments); + }, + + fail: function() { + return env.fail.apply(env, arguments); + }, + + spyOn: function(obj, methodName) { + return env.spyOn(obj, methodName); + }, + + jsApiReporter: new jasmine.JsApiReporter({ + timer: new jasmine.Timer() + }), + + jasmine: jasmine + }; + + jasmine.addCustomEqualityTester = function(tester) { + env.addCustomEqualityTester(tester); + }; + + jasmine.addMatchers = function(matchers) { + return env.addMatchers(matchers); + }; + + jasmine.clock = function() { + return env.clock; + }; + + return jasmineInterface; +}; + getJasmineRequireObj().version = function() { - return '2.0.1'; + return '2.4.1'; }; diff --git a/spec/lib/jasmine-2.0.1/jasmine_favicon.png b/spec/lib/jasmine-2.4.1/jasmine_favicon.png Binary files differindex 3b84583..3b84583 100644 --- a/spec/lib/jasmine-2.0.1/jasmine_favicon.png +++ b/spec/lib/jasmine-2.4.1/jasmine_favicon.png diff --git a/spec/spec/element.js b/spec/spec/element.js index 96a5ff7..1f96120 100644 --- a/spec/spec/element.js +++ b/spec/spec/element.js @@ -645,7 +645,7 @@ describe('Element', function() { it('creates a point from screen coordinates transformed in the elements space', function(){ var rect = draw.rect(100,100) expect(rect.point(2,5).x).toBeCloseTo(-6) - expect(rect.point(2,5).y).toBeCloseTo(-3) + expect(rect.point(2,5).y).toBeCloseTo(-21) }) }) }) diff --git a/spec/spec/event.js b/spec/spec/event.js index b806d65..5390472 100644 --- a/spec/spec/event.js +++ b/spec/spec/event.js @@ -307,7 +307,9 @@ describe('Event', function() { rect3.on('event', action) expect(Object.keys(SVG.listeners[SVG.handlerMap.indexOf(rect.node)]['event']['*']).length).toBe(1) // 1 listener on rect + expect(Object.keys(SVG.listeners[SVG.handlerMap.indexOf(rect2.node)]['event']['*']).length).toBe(1) // 1 listener on rect2 expect(Object.keys(SVG.listeners[SVG.handlerMap.indexOf(rect3.node)]['event']['*']).length).toBe(2) // 2 listener on rect3 + expect(SVG.listeners.length).toBe(listenerCnt + 3) // added listeners on 3 different elements }) if('attaches a handler to a namespaced event', function(){ @@ -338,7 +340,7 @@ describe('Event', function() { }) it('stores the listener for future reference', function() { rect.on('event', action) - expect(SVG.listeners[SVG.handlerMap.indexOf(rect.node)]['event']['*'][action]).not.toBeUndefined() + expect(SVG.listeners[SVG.handlerMap.indexOf(rect.node)]['event']['*'][action._svgjsListenerId]).not.toBeUndefined() }) it('returns the called element', function() { expect(rect.on('event', action)).toBe(rect) diff --git a/spec/spec/fx.js b/spec/spec/fx.js index d9a07ac..cc08731 100644 --- a/spec/spec/fx.js +++ b/spec/spec/fx.js @@ -4,96 +4,62 @@ describe('FX', function() { beforeEach(function() { rect = draw.rect(100,100).move(100,100) fx = rect.animate(500) - fx2 = fx.enqueue() + }) + + afterEach(function() { + //fx.finish() + //rect.off('.fx') }) it('creates an instance of SVG.FX and sets parameter', function() { expect(fx instanceof SVG.FX).toBe(true) - expect(fx.target).toBe(rect) + expect(fx._target).toBe(rect) expect(fx.pos).toBe(0) - expect(fx.paused).toBe(false) - expect(fx.finished).toBe(false) - expect(fx.active).toBe(false) - expect(fx.shared.current).toBe(fx) - expect(fx._next).toBe(fx2) - expect(fx._prev).toBe(null) - expect(fx._duration).toBe(500) - + expect(fx.lastPos).toBe(0) + expect(fx.paused).toBeFalsy() + expect(fx.active).toBeFalsy() + expect(fx.situations).toEqual([]) + expect(fx.current.init).toBeFalsy() + expect(fx.current.reversed).toBeFalsy() + expect(fx.current.duration).toBe(500) + expect(fx.current.delay).toBe(0) + expect(fx.current.animations).toEqual({}) + expect(fx.current.attrs).toEqual({}) + expect(fx.current.styles).toEqual({}) + expect(fx.current.transforms).toEqual([]) + expect(fx.current.once).toEqual({}) }) - describe('current()', function(){ + describe('target()', function(){ it('returns the current fx object', function(){ - expect(fx.current()).toBe(fx.shared.current) - }) - }) - - describe('first()', function(){ - it('returns the first fx object in the queue', function(){ - expect(fx.first()).toBe(fx) - }) - }) - - describe('last()', function(){ - it('returns the last fx object in the queue', function(){ - expect(fx.last()).toBe(fx2) - }) - }) - - describe('next()', function(){ - it('returns the next fx object in the queue', function(){ - expect(fx.next()).toBe(fx2) - }) - it('returns null when it hits the end of the queue', function(){ - expect(fx2.next()).toBe(null) - }) - }) - - describe('prev()', function(){ - it('returns the previous fx object in the queue', function(){ - expect(fx2.prev()).toBe(fx) - }) - it('returns null whn it hits the start of the queue', function(){ - expect(fx.prev()).toBe(null) - }) - }) - - describe('share()', function() { - it('sets a new shared object', function() { - var newObj = {} - var ret = fx.share(newObj) - - expect(fx.shared).toBe(newObj) - expect(ret).toBe(fx) - - // reset the value - fx.share({current:fx}) + expect(fx.target()).toBe(rect) }) }) describe('timeToPos()', function() { it('converts a timestamp to a progress', function() { - expect(fx.timeToPos(fx._start+fx._duration/2)).toBe(0.5) + expect(fx.timeToPos(fx.current.start+fx.current.duration/2)).toBe(0.5) }) }) describe('posToTime()', function() { it('converts a progress to a timestamp', function() { - expect(fx.posToTime(0.5)).toBe(fx._start+fx._duration/2) + expect(fx.posToTime(0.5)).toBe(fx.current.start+fx.current.duration/2) }) }) describe('seek()', function() { it('sets the progress to the specified position', function() { - var start = fx._start + var start = fx.current.start expect(fx.seek(0.5).pos).toBe(0.5) // time is running so we cant compare it directly - expect(fx._start).toBeLessThan(start - fx._duration * 0.5 + 1) - expect(fx._start).toBeGreaterThan(start - fx._duration * 0.5 - 10) + expect(fx.current.start).toBeLessThan(start - fx.current.duration * 0.5 + 1) + expect(fx.current.start).toBeGreaterThan(start - fx.current.duration * 0.5 - 10) }) }) describe('start()', function(){ - it('starts the animation if it is the current', function(done) { + it('starts the animation', function(done) { fx.start() expect(fx.active).toBe(true) expect(fx.timeout).not.toBe(0) @@ -105,17 +71,17 @@ describe('FX', function() { }) describe('pause()', function() { - it('starts the animation if it is the current', function() { + it('pause the animation', function() { expect(fx.pause().paused).toBe(true) }) }) describe('play()', function() { it('unpause the animation', function(done) { - var start = fx.start().pause()._start + var start = fx.start().pause().current.start setTimeout(function(){ expect(fx.play().paused).toBe(false) - expect(fx._start).not.toBe(start) + expect(fx.current.start).not.toBe(start) done() }, 200) }) @@ -123,24 +89,167 @@ describe('FX', function() { describe('speed()', function() { it('speeds up the animation by the given factor', function(){ - //console.log(fx.pos) - expect(fx.speed(2)._duration).toBe(250) - expect(fx.speed(0.5)._duration).toBe(500) - expect(fx.seek(0.2).speed(2)._duration).toBe(0.2 * 500 + 0.8 * 500 / 2) + + expect(fx.speed(2).current.duration).toBe(250) + expect(fx.speed(0.5).current.duration).toBe(500) + expect(fx.seek(0.2).speed(2).current.duration).toBe(0.2 * 500 + 0.8 * 500 / 2) + }) + }) + + describe('reverse()', function() { + it('toggles the direction of the animation without a parameter', function() { + expect(fx.reverse().current.reversed).toBe(true) + }) + }) + + describe('reverse()', function() { + it('sets the direction to backwards with true given', function() { + expect(fx.reverse(true).current.reversed).toBe(true) + }) + }) + + describe('reverse()', function() { + it('sets the direction to forwards with false given', function() { + expect(fx.reverse(false).current.reversed).toBe(false) + }) + }) + + describe('stop()', function() { + it('stops the animation immediately without a parameter', function() { + fx.animate(500) + expect(fx.stop().current).toBeNull() + expect(fx.active).toBeFalsy() + expect(fx.situations.length).toBe(1) + }) + }) + + describe('stop()', function() { + it('stops the animation immediately and fullfill it if first parameter true', function() { + fx.animate(500) + expect(fx.stop(true).current).toBeNull() + expect(fx.active).toBeFalsy() + expect(fx.pos).toBe(1) + expect(fx.situations.length).toBe(1) }) }) - /*describe('reverse()', function() { - it('sets the direction of the animation to -1', function() { - expect(fx.reverse()._direction).toBe(-1) + describe('stop()', function() { + it('stops the animation immediately and remove all items from queue when second parameter true', function() { + fx.animate(500) + expect(fx.stop(false, true).current).toBeNull() + expect(fx.active).toBeFalsy() + expect(fx.situations.length).toBe(0) }) }) describe('finish()', function() { - it('sets the position of the whole animation queue to 1', function() { - expect(fx.finish()._pos).toBe(1) + it('finish the whole animation by fullfilling every single one', function() { + fx.animate(500) + expect(fx.finish().pos).toBe(1) + expect(fx.situations.length).toBe(0) + expect(fx.current).toBeNull() }) - })*/ + }) + + describe('progress()', function() { + it('returns the current position', function() { + expect(fx.progress()).toBe(0) + expect(fx.progress()).toBe(fx.pos) + }) + }) + + describe('after()', function() { + it('adds a callback which is called when the current animation is finished', function(done) { + fx.start().after(function(situation){ + expect(fx.current).toBe(situation) + expect(fx.pos).toBe(1) + done() + }) + }) + }) + + describe('afterAll()', function() { + it('adds a callback which is called when the current animation is finished', function(done) { + fx.start().after(function(){ + expect(fx.pos).toBe(1) + expect(fx.situations.length).toBe(0) + done() + }) + }) + }) + + describe('during()', function() { + it('adds a callback which is called on every animation step', function(done) { + + fx.start().during(function(pos, eased, situation){ + + expect(fx.current).toBe(situation) + + if(fx.pos > 0.9){ + rect.off('.fx') + fx.stop() + + done() + } + }) + }) + }) + + describe('duringAll()', function() { + it('adds a callback which is called on every animation step for the whole chain', function(done) { + + fx.finish() + rect.off('.fx') + + fx.animate(500).start().animate(500) + + var sit = null + + var pos1 = false + var pos2 = false + + setTimeout(function(){ + pos1 = true + }, 300) + + setTimeout(function(){ + pos2 = true + }, 800) + + fx.duringAll(function(pos, eased, fx2, situation){ + + if(pos1){ + pos1 = false + sit = situation + expect(fx2.pos).toBeGreaterThan(0.5) + } + + if(pos2){ + console.log('asd') + pos2 = null + expect(situation).not.toBe(sit) + expect(fx2.pos).toBeGreaterThan(0.5) + done() + } + }) + + setTimeout(function(){ + if(pos2 === null) return + fail('Not enough situations called') + done() + }, 1200) + }) + }) + + describe('once()', function() { + it('adds a callback which is called once at the specified position', function(done) { + + fx.start().once(0.5, function(pos, eased){ + expect(true).toBe(true) + done() + }) + }) + }) it('animates the x/y-attr', function(done) { diff --git a/spec/spec/sugar.js b/spec/spec/sugar.js new file mode 100644 index 0000000..9133032 --- /dev/null +++ b/spec/spec/sugar.js @@ -0,0 +1,679 @@ +describe('Sugar', function() { + + var rect + + beforeEach(function() { + draw.attr('viewBox', null) + }) + + afterEach(function() { + draw.clear() + }) + + describe('fill()', function() { + it('returns the node reference', function() { + rect = draw.rect(100,100) + expect(rect.fill('red')).toBe(rect) + }) + + it('returns the value with no argument given', function() { + rect = draw.rect(100,100).fill('red') + expect(rect.fill('red')).toBe('red') + }) + + describe('color, opacity, rule', function() { + + ['color', 'opacity', 'rule'].forEach(function(a){ + + describe('fill-'+a+'()', function(){ + + rect = draw.rect(100,100).fill('red') + + }) + + }) + + }) + }) + + describe('()', function() { + it('returns the node reference', function() { + rect = draw.rect(100,100) + expect(rect.fill('red')).toBe(rect) + }) + + it('returns the value with no argument given', function() { + rect = draw.rect(100,100).fill('red') + expect(rect.fill('red')).toBe('red') + }) + }) + + describe('attr()', function() { + var rect + + beforeEach(function() { + rect = draw.rect(100,100) + }) + + afterEach(function() { + rect.remove() + }) + + it('sets one attribute when two arguments are given', function() { + rect.attr('fill', '#ff0066') + expect(rect.node.getAttribute('fill')).toBe('#ff0066') + }) + it('sets various attributes when an object is given', function() { + rect.attr({ fill: '#00ff66', stroke: '#ff2233', 'stroke-width': 10 }) + expect(rect.node.getAttribute('fill')).toBe('#00ff66') + expect(rect.node.getAttribute('stroke')).toBe('#ff2233') + expect(rect.node.getAttribute('stroke-width')).toBe('10') + }) + it('gets the value of the string value given as first argument', function() { + rect.attr('fill', '#ff0066') + expect(rect.attr('fill')).toEqual('#ff0066') + }) + it('gets an object with all attributes without any arguments', function() { + rect.attr({ fill: '#00ff66', stroke: '#ff2233' }) + var attr = rect.attr() + expect(attr.fill).toBe('#00ff66') + expect(attr.stroke).toBe('#ff2233') + }) + it('removes an attribute if the second argument is explicitly set to null', function() { + rect.attr('stroke-width', 10) + expect(rect.node.getAttribute('stroke-width')).toBe('10') + rect.attr('stroke-width', null) + expect(rect.node.getAttribute('stroke-width')).toBe(null) + }) + it('correctly parses numeric values as a getter', function() { + rect.attr('stroke-width', 11) + expect(rect.node.getAttribute('stroke-width')).toBe('11') + expect(rect.attr('stroke-width')).toBe(11) + }) + it('correctly parses negative numeric values as a getter', function() { + rect.attr('x', -120) + expect(rect.node.getAttribute('x')).toBe('-120') + expect(rect.attr('x')).toBe(-120) + }) + it('falls back on default values if attribute is not present', function() { + expect(rect.attr('stroke-linejoin')).toBe('miter') + }) + it('gets the "style" attribute as a string', function() { + rect.style('cursor', 'pointer') + expect(rect.node.style.cursor).toBe('pointer') + }) + it('redirects to the style() method when setting a style string', function() { + rect.attr('style', 'cursor:move;') + expect(rect.node.style.cursor).toBe('move') + }) + it('removes style attribute on node if the style is empty', function() { + rect.style('cursor', 'move') + rect.style('cursor', '') + expect(rect.style.cursor).toBe(undefined) + }) + it('acts as a global getter when no arguments are given', function() { + rect.fill('#ff0066') + expect(rect.attr().fill).toBe('#ff0066') + }) + it('correctly parses numeric values as a global getter', function() { + rect.stroke({ width: 20 }) + expect(rect.attr()['stroke-width']).toBe(20) + }) + it('correctly parses negative numeric values as a global getter', function() { + rect.x(-30) + expect(rect.attr().x).toBe(-30) + }) + it('leaves unit values alone as a global getter', function() { + rect.attr('x', '69%') + expect(rect.attr().x).toBe('69%') + }) + }) + + describe('id()', function() { + var rect + + beforeEach(function() { + rect = draw.rect(100,100) + }) + + it('gets the value if the id attribute without an argument', function() { + expect(rect.id()).toBe(rect.attr('id')) + }) + it('sets the value of the id', function() { + rect.id('new_id') + expect(rect.attr('id')).toBe('new_id') + }) + }) + + describe('style()', function() { + it('sets the style with key and value arguments', function() { + var rect = draw.rect(100,100).style('cursor', 'crosshair') + expect(stripped(rect.node.style.cssText)).toBe('cursor:crosshair;') + }) + it('sets multiple styles with an object as the first argument', function() { + var rect = draw.rect(100,100).style({ cursor: 'help', display: 'block' }) + expect(stripped(rect.node.style.cssText)).toMatch(/cursor:help;/) + expect(stripped(rect.node.style.cssText)).toMatch(/display:block;/) + expect(stripped(rect.node.style.cssText).length).toBe(('display:block;cursor:help;').length) + }) + it('gets a style with a string key as the fists argument', function() { + var rect = draw.rect(100,100).style({ cursor: 'progress', display: 'block' }) + expect(rect.style('cursor')).toBe('progress') + }) + it('gets a style with a string key as the fists argument', function() { + var rect = draw.rect(100,100).style({ cursor: 's-resize', display: 'none' }) + expect(stripped(rect.style())).toMatch(/display:none;/) + expect(stripped(rect.style())).toMatch(/cursor:s-resize;/) + expect(stripped(rect.style()).length).toBe(('cursor:s-resize;display:none;').length) + }) + it('removes a style if the value is an empty string', function() { + var rect = draw.rect(100,100).style({ cursor: 'n-resize', display: '' }) + expect(stripped(rect.style())).toBe('cursor:n-resize;') + }) + it('removes a style if the value explicitly set to null', function() { + var rect = draw.rect(100,100).style('cursor', 'w-resize') + expect(stripped(rect.style())).toBe('cursor:w-resize;') + rect.style('cursor', null) + expect(rect.style()).toBe('') + }) + }) + + describe('transform()', function() { + var rect, ctm + + beforeEach(function() { + rect = draw.rect(100,100) + }) + + it('gets the current transformations', function() { + expect(rect.transform()).toEqual(new SVG.Matrix(rect).extract()) + }) + it('sets the translation of and element', function() { + rect.transform({ x: 10, y: 11 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,10,11)') + }) + it('performs an absolute translation', function() { + rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,20,21)') + }) + it('performs a relative translation when relative is set to true', function() { + rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21, relative: true }) + expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,30,32)') + }) + it('performs a relative translation with relative flag', function() { + rect.transform({ x: 10, y: 11 }).transform({ x: 20, y: 21 }, true) + expect(rect.node.getAttribute('transform')).toBe('matrix(1,0,0,1,30,32)') + }) + it('sets the scaleX and scaleY of and element', function() { + rect.transform({ scaleX: 0.5, scaleY: 2 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(0.5,0,0,2,25,-50)') + }) + it('performs a uniform scale with scale given', function() { + rect.transform({ scale: 3 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(3,0,0,3,-100,-100)') + }) + it('performs an absolute scale by default', function() { + rect.transform({ scale: 3 }).transform({ scale: 0.5 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(0.5,0,0,0.5,25,25)') + }) + it('performs a relative scale with a relative flag', function() { + rect.transform({ scaleX: 0.5, scaleY: 2 }).transform({ scaleX: 3, scaleY: 4 }, true) + expect(rect.node.getAttribute('transform')).toBe('matrix(1.5,0,0,8,-25,-350)') + }) + it('sets the skewX of and element with center on the element', function() { + ctm = rect.transform({ skewX: 10 }).ctm() + expect(ctm.a).toBe(1) + expect(ctm.b).toBe(0) + expect(ctm.c).toBeCloseTo(0.17632698070846498) + expect(ctm.d).toBe(1) + expect(ctm.e).toBeCloseTo(-8.81634903542325) + expect(ctm.f).toBe(0) + }) + it('sets the skewX of and element with given center', function() { + ctm = rect.transform({ skewX: 10, cx: 0, cy: 0 }).ctm() + expect(ctm.a).toBe(1) + expect(ctm.b).toBe(0) + expect(ctm.c).toBeCloseTo(0.17632698070846498) + expect(ctm.d).toBe(1) + expect(ctm.e).toBe(0) + expect(ctm.f).toBe(0) + }) + it('sets the skewY of and element', function() { + ctm = rect.transform({ skewY: -10, cx: 0, cy: 0 }).ctm() + expect(ctm.a).toBe(1) + expect(ctm.b).toBeCloseTo(-0.17632698070846498) + expect(ctm.c).toBe(0) + expect(ctm.d).toBe(1) + expect(ctm.e).toBe(0) + expect(ctm.f).toBe(0) + }) + it('rotates the element around its centre if no rotation point is given', function() { + ctm = rect.center(100, 100).transform({ rotation: 45 }).ctm() + expect(ctm.a).toBeCloseTo(0.7071068286895752) + expect(ctm.b).toBeCloseTo(0.7071068286895752) + expect(ctm.c).toBeCloseTo(-0.7071068286895752) + expect(ctm.d).toBeCloseTo(0.7071068286895752) + expect(ctm.e).toBeCloseTo(100) + expect(ctm.f).toBeCloseTo(-41.421356201171875) + expect(rect.transform('rotation')).toBe(45) + }) + it('rotates the element around the given rotation point', function() { + ctm = rect.transform({ rotation: 55, cx: 80, cy:2 }).ctm() + expect(ctm.a).toBeCloseTo(0.5735765099525452) + expect(ctm.b).toBeCloseTo(0.8191521167755127) + expect(ctm.c).toBeCloseTo(-0.8191521167755127) + expect(ctm.d).toBeCloseTo(0.5735765099525452) + expect(ctm.e).toBeCloseTo(35.75218963623047) + expect(ctm.f).toBeCloseTo(-64.67931365966797) + }) + it('transforms element using a matrix', function() { + rect.transform({ a: 0.5, c: 0.5 }) + expect(rect.node.getAttribute('transform')).toBe('matrix(0.5,0,0.5,1,0,0)') + }) + }) + + describe('untransform()', function() { + var circle + + beforeEach(function() { + circle = draw.circle(100).translate(50, 100) + }) + + it('removes the transform attribute', function() { + expect(circle.node.getAttribute('transform')).toBe('matrix(1,0,0,1,50,100)') + circle.untransform() + expect(circle.node.getAttribute('transform')).toBeNull() + }) + it('resets the current transform matix', function() { + expect(circle.ctm()).toEqual(new SVG.Matrix(1,0,0,1,50,100)) + circle.untransform() + expect(circle.ctm()).toEqual(new SVG.Matrix) + }) + }) + + describe('ctm()', function() { + var rect + + beforeEach(function() { + rect = draw.rect(100, 100) + }) + + it('gets the current transform matrix of the element', function() { + rect.translate(10, 20) + expect(rect.ctm().toString()).toBe('matrix(1,0,0,1,10,20)') + }) + it('returns an instance of SVG.Matrix', function() { + expect(rect.ctm() instanceof SVG.Matrix).toBeTruthy() + }) + }) + + describe('data()', function() { + it('sets a data attribute and convert value to json', function() { + var rect = draw.rect(100,100).data('test', 'value') + expect(rect.node.getAttribute('data-test')).toBe('value') + }) + it('sets a data attribute and not convert value to json if flagged raw', function() { + var rect = draw.rect(100,100).data('test', 'value', true) + expect(rect.node.getAttribute('data-test')).toBe('value') + }) + it('sets multiple data attributes and convert values to json when an object is passed', function() { + var rect = draw.rect(100,100).data({ + forbidden: 'fruit' + , multiple: { + values: 'in' + , an: 'object' + } + }) + expect(rect.node.getAttribute('data-forbidden')).toBe('fruit') + expect(rect.node.getAttribute('data-multiple')).toEqual('{"values":"in","an":"object"}') + }) + it('gets data value if only one argument is passed', function() { + var rect = draw.rect(100,100).data('test', 101) + expect(rect.data('test')).toBe(101) + }) + it('maintains data type for a number', function() { + var rect = draw.rect(100,100).data('test', 101) + expect(typeof rect.data('test')).toBe('number') + }) + it('maintains data type for an object', function() { + var rect = draw.rect(100,100).data('test', { string: 'value', array: [1,2,3] }) + expect(typeof rect.data('test')).toBe('object') + expect(Array.isArray(rect.data('test').array)).toBe(true) + }) + }) + + describe('remove()', function() { + it('removes an element and return it', function() { + var rect = draw.rect(100,100) + expect(rect.remove()).toBe(rect) + }) + it('removes an element from its parent', function() { + var rect = draw.rect(100,100) + rect.remove() + expect(draw.has(rect)).toBe(false) + }) + }) + + describe('addTo()', function() { + it('adds an element to a given parent and returns itself', function() { + var rect = draw.rect(100,100) + , group = draw.group() + + expect(rect.addTo(group)).toBe(rect) + expect(rect.parent()).toBe(group) + }) + }) + + describe('putIn()', function() { + it('adds an element to a given parent and returns parent', function() { + var rect = draw.rect(100,100) + , group = draw.group() + + expect(rect.putIn(group)).toBe(group) + expect(rect.parent()).toBe(group) + }) + }) + + describe('rbox()', function() { + it('returns an instance of SVG.RBox', function() { + var rect = draw.rect(100,100) + expect(rect.rbox() instanceof SVG.RBox).toBe(true) + }) + it('returns the correct rectangular box', function() { + var rect = draw.size(200, 150).viewbox(0, 0, 200, 150).rect(105, 210).move(2, 12) + var box = rect.rbox() + expect(box.x).toBeCloseTo(2,0) + expect(box.y).toBeCloseTo(12) + expect(box.cx).toBeCloseTo(54.5) + expect(box.cy).toBeCloseTo(117) + expect(box.width).toBe(105) + expect(box.height).toBe(210) + }) + it('returns the correct rectangular box within a viewbox', function() { + var rect = draw.size(200,150).viewbox(0,0,100,75).rect(105,210).move(2,12) + var box = rect.rbox() + expect(box.x).toBeCloseTo(4) + expect(box.y).toBeCloseTo(24) + expect(box.cx).toBeCloseTo(56.5) + expect(box.cy).toBeCloseTo(129) + expect(box.width).toBe(105) + expect(box.height).toBe(210) + }) + }) + + describe('doc()', function() { + it('returns the parent document', function() { + var rect = draw.rect(100,100) + expect(rect.doc()).toBe(draw) + }) + }) + + describe('parent()', function() { + it('contains the parent svg', function() { + var rect = draw.rect(100,100) + expect(rect.parent()).toBe(draw) + }) + it('contains the parent group when in a group', function() { + var group = draw.group() + , rect = group.rect(100,100) + expect(rect.parent()).toBe(group) + }) + it('contains the parent which matches type', function() { + var group = draw.group() + , rect = group.rect(100,100) + expect(rect.parent(SVG.Doc)).toBe(draw) + }) + it('contains the parent which matches selector', function() { + var group1 = draw.group().addClass('test') + , group2 = group1.group() + , rect = group2.rect(100,100) + expect(rect.parent('.test')).toBe(group1) + }) + }) + + describe('parents()', function() { + it('returns array of parent up to but not including the dom element filtered by type', function() { + var group1 = draw.group().addClass('test') + , group2 = group1.group() + , rect = group2.rect(100,100) + + expect(rect.parents('.test')[0]).toBe(group1) + expect(rect.parents(SVG.G)[0]).toBe(group2) + expect(rect.parents(SVG.G)[1]).toBe(group1) + expect(rect.parents().length).toBe(3) + }) + }) + + describe('clone()', function() { + var rect, group, circle + + beforeEach(function() { + rect = draw.rect(100,100).center(321,567).fill('#f06') + group = draw.group().add(rect) + circle = group.circle(100) + }) + + it('makes an exact copy of the element', function() { + clone = rect.clone() + expect(clone.attr('id', null).attr()).toEqual(rect.attr('id', null).attr()) + }) + it('assigns a new id to the cloned element', function() { + clone = rect.clone() + expect(clone.attr('id')).not.toBe(rect.attr('id')) + }) + it('copies all child nodes as well', function() { + clone = group.clone() + expect(clone.children().length).toBe(group.children().length) + }) + it('assigns a new id to cloned child elements', function() { + clone = group.clone() + expect(clone.attr('id')).not.toEqual(group.attr('id')) + expect(clone.get(0).attr('id')).not.toBe(group.get(0).attr('id')) + expect(clone.get(1).attr('id')).not.toBe(group.get(1).attr('id')) + }) + it('inserts the clone after the cloned element', function() { + clone = rect.clone() + expect(rect.next()).toBe(clone) + }) + }) + + describe('toString()', function() { + it('returns the element id', function() { + var rect = draw.rect(100,100).center(321,567).fill('#f06') + expect(rect + '').toBe(rect.attr('id')) + }) + }) + + describe('replace()', function() { + it('replaces the original element by another given element', function() { + var rect = draw.rect(100,100).center(321,567).fill('#f06') + var circle = draw.circle(200) + var rectIndex = draw.children().indexOf(rect) + + rect.replace(circle) + + expect(rectIndex).toBe(draw.children().indexOf(circle)) + }) + it('removes the original element', function() { + var rect = draw.rect(100,100).center(321,567).fill('#f06') + + rect.replace(draw.circle(200)) + + expect(draw.has(rect)).toBe(false) + }) + it('returns the new element', function() { + var circle = draw.circle(200) + var element = draw.rect(100,100).center(321,567).fill('#f06').replace(circle) + + expect(element).toBe(circle) + }) + }) + + describe('classes()', function() { + it('returns an array of classes on the node', function() { + var element = draw.rect(100,100) + element.node.setAttribute('class', 'one two') + expect(element.classes()).toEqual(['one', 'two']) + }) + }) + + describe('hasClass()', function() { + it('returns true if the node has the class', function() { + var element = draw.rect(100,100) + element.node.setAttribute('class', 'one') + expect(element.hasClass('one')).toBeTruthy() + }) + + it('returns false if the node does not have the class', function() { + var element = draw.rect(100,100) + element.node.setAttribute('class', 'one') + expect(element.hasClass('two')).toBeFalsy() + }) + }) + + describe('addClass()', function() { + it('adds the class to the node', function() { + var element = draw.rect(100,100) + element.addClass('one') + expect(element.hasClass('one')).toBeTruthy() + }) + + it('does not add duplicate classes', function() { + var element = draw.rect(100,100) + element.addClass('one') + element.addClass('one') + expect(element.node.getAttribute('class')).toEqual('one') + }) + + it('returns the svg instance', function() { + var element = draw.rect(100,100) + expect(element.addClass('one')).toEqual(element) + }) + }) + + describe('removeClass()', function() { + it('removes the class from the node when the class exists', function() { + var element = draw.rect(100,100) + element.addClass('one') + element.removeClass('one') + expect(element.hasClass('one')).toBeFalsy() + }) + + it('does nothing when the class does not exist', function() { + var element = draw.rect(100,100) + element.removeClass('one') + expect(element.hasClass('one')).toBeFalsy() + }) + + it('returns the element', function() { + var element = draw.rect(100,100) + expect(element.removeClass('one')).toEqual(element) + }) + }) + + describe('toggleClass()', function() { + it('adds the class when it does not already exist', function(){ + var element = draw.rect(100,100) + element.toggleClass('one') + expect(element.hasClass('one')).toBeTruthy() + }) + it('removes the class when it already exists', function(){ + var element = draw.rect(100,100) + element.addClass('one') + element.toggleClass('one') + expect(element.hasClass('one')).toBeFalsy() + }) + it('returns the svg instance', function() { + var element = draw.rect(100,100) + expect(element.toggleClass('one')).toEqual(element) + }) + }) + + describe('reference()', function() { + it('gets a referenced element from a given attribute', function() { + var rect = draw.defs().rect(100, 100) + , use = draw.use(rect) + , mark = draw.marker(10, 10) + , path = draw.path(svgPath).marker('end', mark) + + expect(use.reference('href')).toBe(rect) + expect(path.reference('marker-end')).toBe(mark) + }) + }) + + describe('svg()', function() { + describe('without an argument', function() { + it('returns full raw svg when called on the main svg doc', function() { + draw.size(100,100).rect(100,100).id(null) + draw.circle(100).fill('#f06').id(null) + expect(draw.svg()).toBe('<svg id="SvgjsSvg1000" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="100" height="100"><rect width="100" height="100"></rect><circle r="50" cx="50" cy="50" fill="#ff0066"></circle></svg>') + }) + it('returns partial raw svg when called on a sub group', function() { + var group = draw.group().id(null) + group.rect(100,100).id(null) + group.circle(100).fill('#f06').id(null) + expect(group.svg()).toBe('<g><rect width="100" height="100"></rect><circle r="50" cx="50" cy="50" fill="#ff0066"></circle></g>') + }) + it('returns a single element when called on an element', function() { + var group = draw.group().id(null) + group.rect(100,100).id(null) + var circle = group.circle(100).fill('#f06').id(null) + expect(circle.svg()).toBe('<circle r="50" cx="50" cy="50" fill="#ff0066"></circle>') + }) + }) + describe('with raw svg given', function() { + it('imports a full svg document', function() { + draw.svg('<svg id="SvgjsSvg1000" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100" viewBox="0 0 50 50"><rect id="SvgjsRect1183" width="100" height="100"></rect><circle id="SvgjsCircle1184" r="50" cx="25" cy="25" fill="#ff0066"></circle></svg>') + expect(draw.get(0).type).toBe('svg') + expect(draw.get(0).children().length).toBe(2) + expect(draw.get(0).get(0).type).toBe('rect') + expect(draw.get(0).get(1).type).toBe('circle') + expect(draw.get(0).get(1).attr('fill')).toBe('#ff0066') + }) + it('imports partial svg content', function() { + draw.svg('<g id="SvgjsG1185"><rect id="SvgjsRect1186" width="100" height="100"></rect><circle id="SvgjsCircle1187" r="50" cx="25" cy="25" fill="#ff0066"></circle></g>') + expect(draw.get(0).type).toBe('g') + expect(draw.get(0).get(0).type).toBe('rect') + expect(draw.get(0).get(1).type).toBe('circle') + expect(draw.get(0).get(1).attr('fill')).toBe('#ff0066') + }) + it('does not import on single elements, even with an argument it acts as a getter', function() { + var rect = draw.rect(100,100).id(null) + , result = rect.svg('<circle r="300"></rect>') + expect(result).toBe('<rect width="100" height="100"></rect>') + }) + }) + }) + + describe('writeDataToDom()', function() { + it('set all properties in el.dom to the svgjs:data attribute', function(){ + var rect = draw.rect(100,100) + rect.dom.foo = 'bar' + rect.dom.number = new SVG.Number('3px') + + rect.writeDataToDom() + + expect(rect.attr('svgjs:data')).toBe('{"foo":"bar","number":"3px"}') + }) + }) + + describe('setData()', function() { + it('read all data from the svgjs:data attribute and assign it to el.dom', function(){ + var rect = draw.rect(100,100) + + rect.attr('svgjs:data', '{"foo":"bar","number":"3px"}') + rect.setData(JSON.parse(rect.attr('svgjs:data'))) + + expect(rect.dom.foo).toBe('bar') + expect(rect.dom.number).toBe('3px') + }) + }) + + describe('point()', function() { + it('creates a point from screen coordinates transformed in the elements space', function(){ + var rect = draw.rect(100,100) + expect(rect.point(2,5).x).toBeCloseTo(-6) + expect(rect.point(2,5).y).toBeCloseTo(-3) + }) + }) +}) |