diff --git a/.gemini.js b/.gemini.js
new file mode 100644
index 00000000..94b7d27c
--- /dev/null
+++ b/.gemini.js
@@ -0,0 +1,20 @@
+module.exports = {
+ rootUrl: "http://yandex.com",
+
+ browsers: {
+ PhantomJS: {
+ desiredCapabilities: {
+ browserName: "phantomjs"
+ }
+ }
+ },
+
+ system: {
+ plugins: {
+ 'html-reporter': {
+ enabled: true,
+ path: 'gemini/reports'
+ }
+ }
+ }
+};
diff --git a/gemini/reports/images/yandex-icons/plain/PhantomJS~current.png b/gemini/reports/images/yandex-icons/plain/PhantomJS~current.png
new file mode 100644
index 00000000..ca695d8e
Binary files /dev/null and b/gemini/reports/images/yandex-icons/plain/PhantomJS~current.png differ
diff --git a/gemini/reports/images/yandex-icons/plain/PhantomJS~ref.png b/gemini/reports/images/yandex-icons/plain/PhantomJS~ref.png
new file mode 100644
index 00000000..3cdb9ded
Binary files /dev/null and b/gemini/reports/images/yandex-icons/plain/PhantomJS~ref.png differ
diff --git a/gemini/reports/images/yandex-search/plain/PhantomJS~ref.png b/gemini/reports/images/yandex-search/plain/PhantomJS~ref.png
new file mode 100644
index 00000000..b9aca6e8
Binary files /dev/null and b/gemini/reports/images/yandex-search/plain/PhantomJS~ref.png differ
diff --git a/gemini/reports/images/yandex-search/with text/PhantomJS~ref.png b/gemini/reports/images/yandex-search/with text/PhantomJS~ref.png
new file mode 100644
index 00000000..3581057c
Binary files /dev/null and b/gemini/reports/images/yandex-search/with text/PhantomJS~ref.png differ
diff --git a/gemini/reports/index.html b/gemini/reports/index.html
new file mode 100644
index 00000000..017e6557
--- /dev/null
+++ b/gemini/reports/index.html
@@ -0,0 +1,221 @@
+
+
+
+ Gemini report
+
+
+
+
+
+ Total Tests 3
+ Passed 2
+ Failed 1
+ Skipped 0
+ Retries 0
+
+
+ Expand all
+ Collapse all
+ Expand errors
+ Show skipped
+ Expand retries
+ Show only diff
+
+
+
+ There are no skipped tests
+
+
+
+
+ yandex-search
+
+
+
+
+
+
+
+ plain
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ with text
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ yandex-icons
+
+
+
+
+
+
+
+ plain
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Error
+ at /home/delphin/workspaces/workspace_vis/vis/node_modules/gemini/lib/browser/client-bridge.js:42:39
+ at tryCatcher (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/util.js:16:23)
+ at Promise._settlePromiseFromHandler (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/promise.js:512:31)
+ at Promise._settlePromise (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/promise.js:569:18)
+ at Promise._settlePromise0 (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/promise.js:614:10)
+ at Promise._settlePromises (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/promise.js:693:18)
+ at Async._drainQueue (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/async.js:133:16)
+ at Async._drainQueues (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/async.js:143:10)
+ at Immediate.Async.drainQueues (/home/delphin/workspaces/workspace_vis/vis/node_modules/bluebird/js/release/async.js:17:14)
+ at runCallback (timers.js:651:20)
+ at tryOnImmediate (timers.js:624:5)
+ at processImmediate [as _immediateCallback] (timers.js:596:5)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gemini/reports/report.css b/gemini/reports/report.css
new file mode 100644
index 00000000..dbd3d411
--- /dev/null
+++ b/gemini/reports/report.css
@@ -0,0 +1,286 @@
+.report {
+ font: 14px Helvetica Neue, Arial, sans-serif;
+}
+
+.summary__key {
+ font-weight: bold;
+ display: inline;
+}
+
+.summary__key::after {
+ content: ':';
+}
+
+.summary__key_has-fails {
+ color: #c00;
+}
+
+.summary__value {
+ margin-left: 5px;
+ margin-right: 20px;
+ display: inline;
+}
+
+.button {
+ background: #fff;
+ border: 1px solid #ccc;
+ border-radius: 2px;
+ outline: 0;
+}
+
+.button:hover {
+ border-color: #555;
+}
+
+.button.pressed,
+.button:active {
+ background: #eee;
+}
+
+.button_checked {
+ background: #ffeba0;
+ border-color: #cebe7d;
+}
+
+.image-box {
+ padding: 5px;
+ border: 1px solid #ccc;
+ background: #c9c9c9;
+}
+
+.image-box__exp-with-act {
+ display: inline-block;
+}
+
+.report_show-only-diff .image-box__exp-with-act {
+ display: none;
+}
+
+.image-box__image {
+ padding: 0 5px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+.section__title {
+ font-weight: bold;
+ cursor: pointer;
+
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.section__title_skipped {
+ color: #ccc;
+ cursor: default;
+
+ -moz-user-select: text;
+ -webkit-user-select: text;
+ -ms-user-select: text;
+ user-select: text;
+}
+
+.section__title:before, .meta-info__switcher:before {
+ display: inline-block;
+ margin-right: 2px;
+ vertical-align: middle;
+ content: '\25bc';
+ color: black;
+}
+
+.section_status_success > .section__title {
+ color: #038035;
+}
+
+.section_status_fail > .section__title {
+ color: #c00;
+}
+
+.section_status_skip > .section__title,
+.section_status_skip > .section__title:hover {
+ color: #ccc;
+ cursor: default;
+}
+
+.section_status_skip > .section__title::before {
+ content: none;
+}
+
+.section__title:hover {
+ color: #2d3e50;
+}
+
+.section__body {
+ padding-left: 15px;
+}
+
+.section__body_guided {
+ border-left: 1px dotted #ccc;
+}
+
+.section_collapsed .section__body {
+ display: none;
+}
+
+.section__icon {
+ display: inline-block;
+ width: 19px;
+ height: 19px;
+ vertical-align: top;
+ padding: 0 3px;
+ border: none;
+ opacity: 0.5;
+ cursor: pointer;
+}
+
+.section__icon:hover {
+ opacity: 1;
+}
+
+.section__icon:before {
+ display: block;
+ width: 100%;
+ height: 100%;
+ content: '';
+ background-repeat: no-repeat;
+ background-size: 100%;
+ background-position: center;
+}
+
+.section__icon_view-local:before {
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgLTAuNzA5IC0zMi4wODEgMTQxLjczMiAxNDEuNzMyIiBoZWlnaHQ9IjE0MS43MzJweCIgaWQ9IkxpdmVsbG9fMSIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSItMC43MDkgLTMyLjA4MSAxNDEuNzMyIDE0MS43MzIiIHdpZHRoPSIxNDEuNzMycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnIGlkPSJMaXZlbGxvXzgwIj48cGF0aCBkPSJNODkuNjY4LDM4Ljc4NmMwLTEwLjc3My04LjczMS0xOS41MTItMTkuNTEtMTkuNTEyUzUwLjY0NiwyOC4wMSw1MC42NDYsMzguNzg2YzAsMTAuNzc0LDguNzMyLDE5LjUxMSwxOS41MTIsMTkuNTExICAgQzgwLjkzNCw1OC4yOTcsODkuNjY4LDQ5LjU2MSw4OS42NjgsMzguNzg2IE0xMjguMzUyLDM4LjcyN2MtMTMuMzE1LDE3LjU5OS0zNC40MjYsMjguOTcyLTU4LjE5MywyOC45NzIgICBjLTIzLjc3LDAtNDQuODc5LTExLjM3My01OC4xOTQtMjguOTcyQzI1LjI3OSwyMS4xMjksNDYuMzg5LDkuNzU2LDcwLjE1OCw5Ljc1NkM5My45MjcsOS43NTYsMTE1LjAzNiwyMS4xMjksMTI4LjM1MiwzOC43MjcgICAgTTE0MC4zMTQsMzguNzZDMTI1LjY2NiwxNS40NzgsOTkuNzI1LDAsNzAuMTU4LDBTMTQuNjQ4LDE1LjQ3OCwwLDM4Ljc2YzE0LjY0OCwyMy4zMTIsNDAuNTkxLDM4LjgxLDcwLjE1OCwzOC44MSAgIFMxMjUuNjY2LDYyLjA3MiwxNDAuMzE0LDM4Ljc2Ii8+PC9nPjxnIGlkPSJMaXZlbGxvXzFfMV8iLz48L3N2Zz4=);
+}
+
+.section__icon_copy-to-clipboard:before {
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDUxMiA1MTIiIGhlaWdodD0iNTEycHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiB3aWR0aD0iNTEycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik00NjguNDkzLDEwMS42MzdMMzcxLjk1NSw1LjA5OEgxNTkuNTd2NzcuMjMxSDQzLjcyNHY0MjQuNzY5aDMwOC45MjN2LTc3LjIzMWgxMTUuODQ2VjEwMS42Mzd6ICAgTTM3MS45NTUsMzIuNDAxbDY5LjIzNiw2OS4yMzVoLTY5LjIzNlYzMi40MDF6IE02My4wMzEsNDg3Ljc5VjEwMS42MzdoMTczLjc2OXY5Ni41MzhoOTYuNTM4VjQ4Ny43OUg2My4wMzF6IE0yNTYuMTA4LDEwOS42MzIgIGw2OS4yMzYsNjkuMjM1aC02OS4yMzZWMTA5LjYzMnogTTM1Mi42NDcsNDEwLjU2VjE3OC44NjdsLTk2LjUzOC05Ni41MzhoLTc3LjIzMVYyNC40MDZoMTczLjc2OXY5Ni41MzhoOTYuNTM4VjQxMC41NkgzNTIuNjQ3eiIgZmlsbD0iIzM3NDA0RCIvPjwvc3ZnPg==);
+}
+
+.section_collapsed .section__title:before,
+.meta-info_collapsed .meta-info__switcher:before {
+ -webkit-transform: rotate(-90deg);
+ -moz-transform: rotate(-90deg);
+ -ms-transform: rotate(-90deg);
+ -o-transform: rotate(-90deg);
+ transform: rotate(-90deg);
+}
+
+.reason {
+ background: #f6f5f3;
+ border: 1px solid #ccc;
+ padding: 5px;
+ font: 12px Consolas, Monaco, monospace;
+}
+
+.state-button {
+ position: relative;
+ height: 22px;
+ display: inline-block;
+ box-shadow: 0 0 1px #000;
+ border: 1px solid #fff;
+ outline: none;
+ background: #fff;
+ cursor: pointer;
+}
+
+.cswitcher__item, .tab-switcher__button {
+ width: 22px;
+}
+
+.cswitcher__item_selected.cswitcher__item::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ background: rgba(4, 4, 4, 0.3) no-repeat 3px 2px url('data:image/svg+xml;utf8, ');
+ height: 20px;
+ width: 20px;
+}
+
+.cswitcher_color_1 {
+ background: #c9c9c9;
+}
+
+.cswitcher_color_2 {
+ background: #d5ff09;
+}
+
+.cswitcher_color_3 {
+ background-image: url('data:image/svg+xml;utf8, ');
+}
+
+.collapsed {
+ display: none;
+}
+
+.skipped__list {
+ margin: 10px 0;
+ font-weight: bold;
+ color: #ccc;
+}
+
+a:link,
+a:visited {
+ text-decoration: none;
+}
+
+a:hover,
+a:active {
+ text-decoration: underline;
+}
+
+.tab__item {
+ display: none;
+}
+
+.tab__item_active {
+ display: block;
+}
+
+.tab-switcher, .cswitcher {
+ display: inline-block;
+ vertical-align: top;
+ padding: 5px;
+}
+
+.cswitcher:before {
+ content: 'Background:';
+ padding-right: 4px;
+}
+
+.tab-switcher:before {
+ content: 'Attempts:';
+ padding-right: 4px;
+}
+
+.tab-switcher__button_active {
+ background: #ffeba0;
+}
+
+.meta-info {
+ margin: 5px;
+}
+
+.meta-info__switcher {
+ display: inline-block;
+ cursor: pointer;
+}
+
+.meta-info__content {
+ margin: 5px 0;
+ background: #f6f5f3;
+ padding: 5px;
+}
+
+.meta-info_collapsed .meta-info__content {
+ display: none;
+}
+
+.text-input {
+ outline: 0;
+ line-height: 14px;
+ padding: 2px 5px;
+ border: 1px solid #ccc;
+ border-radius: 2px;
+}
diff --git a/gemini/reports/report.min.js b/gemini/reports/report.min.js
new file mode 100644
index 00000000..d7d3b6f5
--- /dev/null
+++ b/gemini/reports/report.min.js
@@ -0,0 +1,2 @@
+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o .section__body > .image-box .tab__item_active img");forEach.call(sections,function(section){if(section.classList.contains("section_status_fail")){section.classList.remove("section_collapsed")}else{section.classList.add("section_collapsed")}})}function expandRetries(){loadLazyImages(document,".has-retries > .section__body > .image-box .tab__item_active img");forEach.call(sections,function(section){if(section.classList.contains("has-retries")){section.classList.remove("section_collapsed")}else{section.classList.add("section_collapsed")}})}function handleColorSwitch(target,sources){var imageBox=findClosest(target,"image-box");sources.forEach(function(item){item.classList.remove("cswitcher__item_selected")});forEach.call(imageBox.classList,function(cls){if(/cswitcher_color_\d+/.test(cls)){imageBox.classList.remove(cls)}});target.classList.add("cswitcher__item_selected");imageBox.classList.add("cswitcher_color_"+target.dataset.id)}function handleRetriesSwitch(target){var imageBox=target.closest(".image-box");switch_(imageBox.querySelector(".tab"),"tab__item_active");switch_(imageBox.querySelector(".tab-switcher"),"tab-switcher__button_active");loadLazyImages(imageBox,".tab__item_active img");function switch_(elem,selector){forEach.call(elem.children,function(item){item.classList.remove(selector);if(target.getAttribute("data-position")===item.getAttribute("data-position")){item.classList.add(selector)}})}}function bodyClick(e){var target=e.target;if(target.classList.contains("cswitcher__item")){handleColorSwitch(target,filter.call(target.parentNode.childNodes,function(node){return node.nodeType===Node.ELEMENT_NODE}))}if(target.classList.contains("tab-switcher__button")){handleRetriesSwitch(target)}if(target.classList.contains("meta-info__switcher")){toggleMetaInfo(target)}}function toggleMetaInfo(target){target.closest(".meta-info").classList.toggle("meta-info_collapsed")}function showOnlyDiff(e){e.target.classList.toggle("button_checked");document.body.classList.toggle("report_show-only-diff")}function findClosest(context,cls){while(context=context.parentNode){if(context.classList.contains(cls)){return context}}}function loadLazyImages(elem,selector){forEach.call(elem.querySelectorAll(selector),function(img){if(img.dataset.src&&img.src!==img.dataset.src){img.src=img.dataset.src}})}function showSkippedList(){document.getElementById("showSkipped").classList.toggle("pressed");document.getElementById("skippedList").classList.toggle("collapsed")}function handleHostChange(){var textInput=document.getElementById("viewHostInput");var viewButtons=document.querySelectorAll(".section__icon_view-local");textInput.addEventListener("change",function(){setViewLinkHost(textInput.value);if(window.localStorage){window.localStorage.setItem("_gemini-replace-host",textInput.value)}});if(window.localStorage){var host=window.localStorage.getItem("_gemini-replace-host");if(host){setViewLinkHost(host);textInput.value=host}}function setViewLinkHost(host){viewButtons.forEach(function(item){var href=item.dataset.suiteViewLink,parsedHost;if(host){parsedHost=url.parse(host,false,true);href=url.format(Object.assign(url.parse(href),{host:parsedHost.slashes?parsedHost.host:host,protocol:parsedHost.slashes?parsedHost.protocol:null,hostname:null,port:null}))}item.setAttribute("href",href)})}}function handleClipboard(){forEach.call(document.querySelectorAll(".section__icon_copy-to-clipboard"),function(clipboard){new Clipboard(clipboard)})}document.addEventListener("DOMContentLoaded",function(){document.getElementById("expandAll").addEventListener("click",expandAll);document.getElementById("collapseAll").addEventListener("click",collapseAll);document.getElementById("expandErrors").addEventListener("click",expandErrors);document.getElementById("showSkipped").addEventListener("click",showSkippedList);document.getElementById("showRetries").addEventListener("click",expandRetries);document.getElementById("showOnlyDiff").addEventListener("click",showOnlyDiff);document.body.addEventListener("click",bodyClick);forEach.call(document.querySelectorAll(".section"),function(section){section.querySelector(".section__title").addEventListener("click",function(){loadLazyImages(section,":scope > .section__body > .image-box .tab__item_active img");section.classList.toggle("section_collapsed")})});forEach.call(document.querySelectorAll(".button"),function(button){button.addEventListener("click",function(e){e.stopPropagation()})})});handleClipboard();handleHostChange();expandErrors()})()},{clipboard:9,url:6}],2:[function(require,module,exports){(function(global){(function(root){var freeExports=typeof exports=="object"&&exports&&!exports.nodeType&&exports;var freeModule=typeof module=="object"&&module&&!module.nodeType&&module;var freeGlobal=typeof global=="object"&&global;if(freeGlobal.global===freeGlobal||freeGlobal.window===freeGlobal||freeGlobal.self===freeGlobal){root=freeGlobal}var punycode,maxInt=2147483647,base=36,tMin=1,tMax=26,skew=38,damp=700,initialBias=72,initialN=128,delimiter="-",regexPunycode=/^xn--/,regexNonASCII=/[^\x20-\x7E]/,regexSeparators=/[\x2E\u3002\uFF0E\uFF61]/g,errors={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},baseMinusTMin=base-tMin,floor=Math.floor,stringFromCharCode=String.fromCharCode,key;function error(type){throw new RangeError(errors[type])}function map(array,fn){var length=array.length;var result=[];while(length--){result[length]=fn(array[length])}return result}function mapDomain(string,fn){var parts=string.split("@");var result="";if(parts.length>1){result=parts[0]+"@";string=parts[1]}string=string.replace(regexSeparators,".");var labels=string.split(".");var encoded=map(labels,fn).join(".");return result+encoded}function ucs2decode(string){var output=[],counter=0,length=string.length,value,extra;while(counter=55296&&value<=56319&&counter65535){value-=65536;output+=stringFromCharCode(value>>>10&1023|55296);value=56320|value&1023}output+=stringFromCharCode(value);return output}).join("")}function basicToDigit(codePoint){if(codePoint-48<10){return codePoint-22}if(codePoint-65<26){return codePoint-65}if(codePoint-97<26){return codePoint-97}return base}function digitToBasic(digit,flag){return digit+22+75*(digit<26)-((flag!=0)<<5)}function adapt(delta,numPoints,firstTime){var k=0;delta=firstTime?floor(delta/damp):delta>>1;delta+=floor(delta/numPoints);for(;delta>baseMinusTMin*tMax>>1;k+=base){delta=floor(delta/baseMinusTMin)}return floor(k+(baseMinusTMin+1)*delta/(delta+skew))}function decode(input){var output=[],inputLength=input.length,out,i=0,n=initialN,bias=initialBias,basic,j,index,oldi,w,k,digit,t,baseMinusT;basic=input.lastIndexOf(delimiter);if(basic<0){basic=0}for(j=0;j=128){error("not-basic")}output.push(input.charCodeAt(j))}for(index=basic>0?basic+1:0;index=inputLength){error("invalid-input")}digit=basicToDigit(input.charCodeAt(index++));if(digit>=base||digit>floor((maxInt-i)/w)){error("overflow")}i+=digit*w;t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(digitfloor(maxInt/baseMinusT)){error("overflow")}w*=baseMinusT}out=output.length+1;bias=adapt(i-oldi,out,oldi==0);if(floor(i/out)>maxInt-n){error("overflow")}n+=floor(i/out);i%=out;output.splice(i++,0,n)}return ucs2encode(output)}function encode(input){var n,delta,handledCPCount,basicLength,bias,j,m,q,k,t,currentValue,output=[],inputLength,handledCPCountPlusOne,baseMinusT,qMinusT;input=ucs2decode(input);inputLength=input.length;n=initialN;delta=0;bias=initialBias;for(j=0;j=n&¤tValuefloor((maxInt-delta)/handledCPCountPlusOne)){error("overflow")}delta+=(m-n)*handledCPCountPlusOne;n=m;for(j=0;jmaxInt){error("overflow")}if(currentValue==n){for(q=delta,k=base;;k+=base){t=k<=bias?tMin:k>=bias+tMax?tMax:k-bias;if(q0&&len>maxKeys){len=maxKeys}for(var i=0;i=0){kstr=x.substr(0,idx);vstr=x.substr(idx+1)}else{kstr=x;vstr=""}k=decodeURIComponent(kstr);v=decodeURIComponent(vstr);if(!hasOwnProperty(obj,k)){obj[k]=v}else if(isArray(obj[k])){obj[k].push(v)}else{obj[k]=[obj[k],v]}}return obj};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"}},{}],4:[function(require,module,exports){"use strict";var stringifyPrimitive=function(v){switch(typeof v){case"string":return v;case"boolean":return v?"true":"false";case"number":return isFinite(v)?v:"";default:return""}};module.exports=function(obj,sep,eq,name){sep=sep||"&";eq=eq||"=";if(obj===null){obj=undefined}if(typeof obj==="object"){return map(objectKeys(obj),function(k){var ks=encodeURIComponent(stringifyPrimitive(k))+eq;if(isArray(obj[k])){return map(obj[k],function(v){return ks+encodeURIComponent(stringifyPrimitive(v))}).join(sep)}else{return ks+encodeURIComponent(stringifyPrimitive(obj[k]))}}).join(sep)}if(!name)return"";return encodeURIComponent(stringifyPrimitive(name))+eq+encodeURIComponent(stringifyPrimitive(obj))};var isArray=Array.isArray||function(xs){return Object.prototype.toString.call(xs)==="[object Array]"};function map(xs,f){if(xs.map)return xs.map(f);var res=[];for(var i=0;i",'"',"`"," ","\r","\n","\t"],unwise=["{","}","|","\\","^","`"].concat(delims),autoEscape=["'"].concat(unwise),nonHostChars=["%","/","?",";","#"].concat(autoEscape),hostEndingChars=["/","?","#"],hostnameMaxLen=255,hostnamePartPattern=/^[+a-z0-9A-Z_-]{0,63}$/,hostnamePartStart=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,unsafeProtocol={javascript:true,"javascript:":true},hostlessProtocol={javascript:true,"javascript:":true},slashedProtocol={http:true,https:true,ftp:true,gopher:true,file:true,"http:":true,"https:":true,"ftp:":true,"gopher:":true,"file:":true},querystring=require("querystring");function urlParse(url,parseQueryString,slashesDenoteHost){if(url&&util.isObject(url)&&url instanceof Url)return url;var u=new Url;u.parse(url,parseQueryString,slashesDenoteHost);return u}Url.prototype.parse=function(url,parseQueryString,slashesDenoteHost){if(!util.isString(url)){throw new TypeError("Parameter 'url' must be a string, not "+typeof url)}var queryIndex=url.indexOf("?"),splitter=queryIndex!==-1&&queryIndex127){newpart+="x"}else{newpart+=part[j]}}if(!newpart.match(hostnamePartPattern)){var validParts=hostparts.slice(0,i);var notHost=hostparts.slice(i+1);var bit=part.match(hostnamePartStart);if(bit){validParts.push(bit[1]);notHost.unshift(bit[2])}if(notHost.length){rest="/"+notHost.join(".")+rest}this.hostname=validParts.join(".");break}}}}if(this.hostname.length>hostnameMaxLen){this.hostname=""}else{this.hostname=this.hostname.toLowerCase()}if(!ipv6Hostname){this.hostname=punycode.toASCII(this.hostname)}var p=this.port?":"+this.port:"";var h=this.hostname||"";this.host=h+p;this.href+=this.host;if(ipv6Hostname){this.hostname=this.hostname.substr(1,this.hostname.length-2);if(rest[0]!=="/"){rest="/"+rest}}}if(!unsafeProtocol[lowerProto]){for(var i=0,l=autoEscape.length;i0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}result.search=relative.search;result.query=relative.query;if(!util.isNull(result.pathname)||!util.isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.href=result.format();return result}if(!srcPath.length){result.pathname=null;if(result.search){result.path="/"+result.search}else{result.path=null}result.href=result.format();return result}var last=srcPath.slice(-1)[0];var hasTrailingSlash=(result.host||relative.host||srcPath.length>1)&&(last==="."||last==="..")||last==="";var up=0;for(var i=srcPath.length;i>=0;i--){last=srcPath[i];if(last==="."){srcPath.splice(i,1)}else if(last===".."){srcPath.splice(i,1);up++}else if(up){srcPath.splice(i,1);up--}}if(!mustEndAbs&&!removeAllDots){for(;up--;up){srcPath.unshift("..")}}if(mustEndAbs&&srcPath[0]!==""&&(!srcPath[0]||srcPath[0].charAt(0)!=="/")){srcPath.unshift("")}if(hasTrailingSlash&&srcPath.join("/").substr(-1)!=="/"){srcPath.push("")}var isAbsolute=srcPath[0]===""||srcPath[0]&&srcPath[0].charAt(0)==="/";if(psychotic){result.hostname=result.host=isAbsolute?"":srcPath.length?srcPath.shift():"";var authInHost=result.host&&result.host.indexOf("@")>0?result.host.split("@"):false;if(authInHost){result.auth=authInHost.shift();result.host=result.hostname=authInHost.shift()}}mustEndAbs=mustEndAbs||result.host&&srcPath.length;if(mustEndAbs&&!isAbsolute){srcPath.unshift("")}if(!srcPath.length){result.pathname=null;result.path=null}else{result.pathname=srcPath.join("/")}if(!util.isNull(result.pathname)||!util.isNull(result.search)){result.path=(result.pathname?result.pathname:"")+(result.search?result.search:"")}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==":"){this.port=port.substr(1)}host=host.substr(0,host.length-port.length)}if(host)this.hostname=host}},{"./util":7,punycode:2,querystring:5}],7:[function(require,module,exports){"use strict";module.exports={isString:function(arg){return typeof arg==="string"},isObject:function(arg){return typeof arg==="object"&&arg!==null},isNull:function(arg){return arg===null},isNullOrUndefined:function(arg){return arg==null}}},{}],8:[function(require,module,exports){(function(global,factory){if(typeof define==="function"&&define.amd){define(["module","select"],factory)}else if(typeof exports!=="undefined"){factory(module,require("select"))}else{var mod={exports:{}};factory(mod,global.select);global.clipboardAction=mod.exports}})(this,function(module,_select){"use strict";var _select2=_interopRequireDefault(_select);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}var _typeof=typeof Symbol==="function"&&typeof Symbol.iterator==="symbol"?function(obj){return typeof obj}:function(obj){return obj&&typeof Symbol==="function"&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj};function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var _createClass=function(){function defineProperties(target,props){for(var i=0;i0&&arguments[0]!==undefined?arguments[0]:{};this.action=options.action;this.emitter=options.emitter;this.target=options.target;this.text=options.text;this.trigger=options.trigger;this.selectedText=""}},{key:"initSelection",value:function initSelection(){if(this.text){this.selectFake()}else if(this.target){this.selectTarget()}}},{key:"selectFake",value:function selectFake(){var _this=this;var isRTL=document.documentElement.getAttribute("dir")=="rtl";this.removeFake();this.fakeHandlerCallback=function(){return _this.removeFake()};this.fakeHandler=document.body.addEventListener("click",this.fakeHandlerCallback)||true;this.fakeElem=document.createElement("textarea");this.fakeElem.style.fontSize="12pt";this.fakeElem.style.border="0";this.fakeElem.style.padding="0";this.fakeElem.style.margin="0";this.fakeElem.style.position="absolute";this.fakeElem.style[isRTL?"right":"left"]="-9999px";var yPosition=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.addEventListener("focus",window.scrollTo(0,yPosition));this.fakeElem.style.top=yPosition+"px";this.fakeElem.setAttribute("readonly","");this.fakeElem.value=this.text;document.body.appendChild(this.fakeElem);this.selectedText=(0,_select2.default)(this.fakeElem);this.copyText()}},{key:"removeFake",value:function removeFake(){if(this.fakeHandler){document.body.removeEventListener("click",this.fakeHandlerCallback);this.fakeHandler=null;this.fakeHandlerCallback=null}if(this.fakeElem){document.body.removeChild(this.fakeElem);this.fakeElem=null}}},{key:"selectTarget",value:function selectTarget(){this.selectedText=(0,_select2.default)(this.target);this.copyText()}},{key:"copyText",value:function copyText(){var succeeded=void 0;try{succeeded=document.execCommand(this.action)}catch(err){succeeded=false}this.handleResult(succeeded)}},{key:"handleResult",value:function handleResult(succeeded){this.emitter.emit(succeeded?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function clearSelection(){if(this.target){this.target.blur()}window.getSelection().removeAllRanges()}},{key:"destroy",value:function destroy(){this.removeFake()}},{key:"action",set:function set(){var action=arguments.length>0&&arguments[0]!==undefined?arguments[0]:"copy";this._action=action;if(this._action!=="copy"&&this._action!=="cut"){throw new Error('Invalid "action" value, use either "copy" or "cut"')}},get:function get(){return this._action}},{key:"target",set:function set(target){if(target!==undefined){if(target&&(typeof target==="undefined"?"undefined":_typeof(target))==="object"&&target.nodeType===1){if(this.action==="copy"&&target.hasAttribute("disabled")){throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute')}if(this.action==="cut"&&(target.hasAttribute("readonly")||target.hasAttribute("disabled"))){throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}this._target=target}else{throw new Error('Invalid "target" value, use a valid Element')}}},get:function get(){return this._target}}]);return ClipboardAction}();module.exports=ClipboardAction})},{select:14}],9:[function(require,module,exports){(function(global,factory){if(typeof define==="function"&&define.amd){define(["module","./clipboard-action","tiny-emitter","good-listener"],factory)}else if(typeof exports!=="undefined"){factory(module,require("./clipboard-action"),require("tiny-emitter"),require("good-listener"))}else{var mod={exports:{}};factory(mod,global.clipboardAction,global.tinyEmitter,global.goodListener);global.clipboard=mod.exports}})(this,function(module,_clipboardAction,_tinyEmitter,_goodListener){"use strict";var _clipboardAction2=_interopRequireDefault(_clipboardAction);var _tinyEmitter2=_interopRequireDefault(_tinyEmitter);var _goodListener2=_interopRequireDefault(_goodListener);function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var _createClass=function(){function defineProperties(target,props){for(var i=0;i0&&arguments[0]!==undefined?arguments[0]:{};this.action=typeof options.action==="function"?options.action:this.defaultAction;this.target=typeof options.target==="function"?options.target:this.defaultTarget;this.text=typeof options.text==="function"?options.text:this.defaultText}},{key:"listenClick",value:function listenClick(trigger){var _this2=this;this.listener=(0,_goodListener2.default)(trigger,"click",function(e){return _this2.onClick(e)})}},{key:"onClick",value:function onClick(e){var trigger=e.delegateTarget||e.currentTarget;if(this.clipboardAction){this.clipboardAction=null}this.clipboardAction=new _clipboardAction2.default({action:this.action(trigger),target:this.target(trigger),text:this.text(trigger),trigger:trigger,emitter:this})}},{key:"defaultAction",value:function defaultAction(trigger){return getAttributeValue("action",trigger)}},{key:"defaultTarget",value:function defaultTarget(trigger){var selector=getAttributeValue("target",trigger);if(selector){return document.querySelector(selector)}}},{key:"defaultText",value:function defaultText(trigger){return getAttributeValue("text",trigger)}},{key:"destroy",value:function destroy(){this.listener.destroy();if(this.clipboardAction){this.clipboardAction.destroy();this.clipboardAction=null}}}]);return Clipboard}(_tinyEmitter2.default);function getAttributeValue(suffix,element){var attribute="data-clipboard-"+suffix;if(!element.hasAttribute(attribute)){return}return element.getAttribute(attribute)}module.exports=Clipboard})},{"./clipboard-action":8,"good-listener":13,"tiny-emitter":15}],10:[function(require,module,exports){var DOCUMENT_NODE_TYPE=9;if(typeof Element!=="undefined"&&!Element.prototype.matches){var proto=Element.prototype;proto.matches=proto.matchesSelector||proto.mozMatchesSelector||proto.msMatchesSelector||proto.oMatchesSelector||proto.webkitMatchesSelector}function closest(element,selector){while(element&&element.nodeType!==DOCUMENT_NODE_TYPE){if(element.matches(selector))return element;element=element.parentNode}}module.exports=closest},{}],11:[function(require,module,exports){var closest=require("./closest");function delegate(element,selector,type,callback,useCapture){var listenerFn=listener.apply(this,arguments);element.addEventListener(type,listenerFn,useCapture);return{destroy:function(){element.removeEventListener(type,listenerFn,useCapture)}}}function listener(element,selector,type,callback){return function(e){e.delegateTarget=closest(e.target,selector);if(e.delegateTarget){callback.call(element,e)}}}module.exports=delegate},{"./closest":10}],12:[function(require,module,exports){exports.node=function(value){return value!==undefined&&value instanceof HTMLElement&&value.nodeType===1};exports.nodeList=function(value){var type=Object.prototype.toString.call(value);return value!==undefined&&(type==="[object NodeList]"||type==="[object HTMLCollection]")&&"length"in value&&(value.length===0||exports.node(value[0]))};exports.string=function(value){return typeof value==="string"||value instanceof String};exports.fn=function(value){var type=Object.prototype.toString.call(value);return type==="[object Function]"}},{}],13:[function(require,module,exports){var is=require("./is");var delegate=require("delegate");function listen(target,type,callback){if(!target&&!type&&!callback){throw new Error("Missing required arguments")}if(!is.string(type)){throw new TypeError("Second argument must be a String")}if(!is.fn(callback)){throw new TypeError("Third argument must be a Function")}if(is.node(target)){return listenNode(target,type,callback)}else if(is.nodeList(target)){return listenNodeList(target,type,callback)}else if(is.string(target)){return listenSelector(target,type,callback)}else{throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}}function listenNode(node,type,callback){node.addEventListener(type,callback);return{destroy:function(){node.removeEventListener(type,callback)}}}function listenNodeList(nodeList,type,callback){Array.prototype.forEach.call(nodeList,function(node){node.addEventListener(type,callback)});return{destroy:function(){Array.prototype.forEach.call(nodeList,function(node){node.removeEventListener(type,callback)})}}}function listenSelector(selector,type,callback){return delegate(document.body,selector,type,callback)}module.exports=listen},{"./is":12,delegate:11}],14:[function(require,module,exports){function select(element){var selectedText;if(element.nodeName==="SELECT"){element.focus();selectedText=element.value}else if(element.nodeName==="INPUT"||element.nodeName==="TEXTAREA"){var isReadOnly=element.hasAttribute("readonly");if(!isReadOnly){element.setAttribute("readonly","")}element.select();element.setSelectionRange(0,element.value.length);if(!isReadOnly){element.removeAttribute("readonly")}selectedText=element.value}else{if(element.hasAttribute("contenteditable")){element.focus()}var selection=window.getSelection();var range=document.createRange();range.selectNodeContents(element);selection.removeAllRanges();selection.addRange(range);selectedText=selection.toString()}return selectedText}module.exports=select},{}],15:[function(require,module,exports){function E(){}E.prototype={on:function(name,callback,ctx){var e=this.e||(this.e={});(e[name]||(e[name]=[])).push({fn:callback,ctx:ctx});return this},once:function(name,callback,ctx){var self=this;function listener(){self.off(name,listener);callback.apply(ctx,arguments)}listener._=callback;return this.on(name,listener,ctx)},emit:function(name){var data=[].slice.call(arguments,1);var evtArr=((this.e||(this.e={}))[name]||[]).slice();var i=0;var len=evtArr.length;for(i;i= 0) {
+ gutil.log("Started phantomjs webdriver");
+
+ var geminiProcess = spawn('gemini', ['test', 'test/gemini']);
+ geminiProcess.stdout.on('data', function(data) {
+ var msg = data.toString().replace(/\n$/g, '');
+ if (msg.startsWith('✓')) {
+ gutil.log(gutil.colors.green(msg));
+ } else if (msg.startsWith('✘')) {
+ hasError = true;
+ gutil.log(gutil.colors.red(msg));
+ } else {
+ gutil.log(msg);
+ }
+ });
+ geminiProcess.stderr.on('data', function(data) {
+ if (!(data.toString().indexOf('DeprecationWarning:') >= 0)) {
+ hasError = true;
+ gutil.log(gutil.colors.red(data.toString().replace(/\n$/g,
+ '')));
+ }
+ });
+ geminiProcess.on('close', function(code) {
+ completed = true;
+ phantomjsProcess.kill();
+ });
+ }
+ });
+ phantomjsProcess.stderr.on('data', function(data) {
+ gutil.log(gutil.colors.red(data));
+ });
+ phantomjsProcess.on('close', function(code) {
+ if (code && !completed) {
+ return cb(new Error('✘ phantomjs failed with code: ' + code +
+ '\n' +
+ 'Check that port ' + phantomjsPort +
+ ' is free and that there are no other ' +
+ 'instances of phantomjs running. (`killall phantomjs`)'));
+ }
+ gutil.log("Stoped phantomjs webdriver");
+
+ if (hasError) {
+ opn('./gemini/reports/index.html');
+ }
+
+ cb();
+ });
+});
+
// clean the dist/img directory
-gulp.task('clean', function (cb) {
+gulp.task('clean', function(cb) {
rimraf(DIST + '/img', cb);
});
-gulp.task('bundle-js', function (cb) {
+gulp.task('bundle-js', function(cb) {
// update the banner contents (has a date in it which should stay up to date)
bannerPlugin.banner = createBanner();
- compiler.run(function (err, stats) {
+ compiler.run(function(err, stats) {
handleCompilerCallback(err, stats);
cb();
});
});
// create individual bundles for timeline+graph2d, network, graph3d
-gulp.task('bundle-js-individual', function (cb) {
+gulp.task('bundle-js-individual', function(cb) {
// update the banner contents (has a date in it which should stay up to date)
bannerPlugin.banner = createBanner();
- async.each(INDIVIDUAL_JS_BUNDLES, function (item, callback) {
+ async.each(INDIVIDUAL_JS_BUNDLES, function(item, callback) {
var webpackTimelineConfig = {
entry: item.entry,
output: {
@@ -138,12 +203,12 @@ gulp.task('bundle-js-individual', function (cb) {
sourcePrefix: ' '
},
module: webpackModule,
- plugins: [ bannerPlugin, new webpack.optimize.UglifyJsPlugin() ],
+ plugins: [bannerPlugin, new webpack.optimize.UglifyJsPlugin()],
cache: true
};
var compiler = webpack(webpackTimelineConfig);
- compiler.run(function (err, stats) {
+ compiler.run(function(err, stats) {
handleCompilerCallback(err, stats);
callback();
});
@@ -152,48 +217,51 @@ gulp.task('bundle-js-individual', function (cb) {
});
// bundle and minify css
-gulp.task('bundle-css', function () {
+gulp.task('bundle-css', function() {
return gulp.src('./lib/**/*.css')
- .pipe(concat(VIS_CSS))
- .pipe(gulp.dest(DIST))
- // TODO: nicer to put minifying css in a separate task?
- .pipe(cleanCSS())
- .pipe(rename(VIS_MIN_CSS))
- .pipe(gulp.dest(DIST));
+ .pipe(concat(VIS_CSS))
+ .pipe(gulp.dest(DIST))
+ // TODO: nicer to put minifying css in a separate task?
+ .pipe(cleanCSS())
+ .pipe(rename(VIS_MIN_CSS))
+ .pipe(gulp.dest(DIST));
});
// bundle and minify individual css
-gulp.task('bundle-css-individual', function (cb) {
- async.each(INDIVIDUAL_CSS_BUNDLES, function (item, callback) {
+gulp.task('bundle-css-individual', function(cb) {
+ async.each(INDIVIDUAL_CSS_BUNDLES, function(item, callback) {
return gulp.src(item.entry)
- .pipe(concat(item.filename))
- .pipe(cleanCSS())
- .pipe(rename(item.filename))
- .pipe(gulp.dest(DIST))
- .on('end', callback);
+ .pipe(concat(item.filename))
+ .pipe(cleanCSS())
+ .pipe(rename(item.filename))
+ .pipe(gulp.dest(DIST))
+ .on('end', callback);
}, cb);
});
-gulp.task('copy', ['clean'], function () {
- var network = gulp.src('./lib/network/img/**/*')
- .pipe(gulp.dest(DIST + '/img/network'));
+gulp.task('copy', ['clean'], function() {
+ var network = gulp.src('./lib/network/img/**/*')
+ .pipe(gulp.dest(DIST + '/img/network'));
- return network;
+ return network;
});
-gulp.task('minify', ['bundle-js'], function (cb) {
+gulp.task('minify', ['bundle-js'], function(cb) {
var result = uglify.minify([DIST + '/' + VIS_JS], uglifyConfig);
// note: we add a newline '\n' to the end of the minified file to prevent
// any issues when concatenating the file downstream (the file ends
// with a comment).
fs.writeFileSync(DIST + '/' + VIS_MIN_JS, result.code + '\n');
- fs.writeFileSync(DIST + '/' + VIS_MAP, result.map.replace(/"\.\/dist\//g, '"'));
+ fs.writeFileSync(DIST + '/' + VIS_MAP, result.map.replace(/"\.\/dist\//g,
+ '"'));
cb();
});
-gulp.task('bundle', ['bundle-js', 'bundle-js-individual', 'bundle-css', 'bundle-css-individual', 'copy']);
+gulp.task('bundle', ['bundle-js', 'bundle-js-individual', 'bundle-css',
+ 'bundle-css-individual', 'copy'
+]);
// read command line arguments --bundle and --minify
var bundle = 'bundle' in argv;
@@ -204,14 +272,13 @@ if (bundle || minify) {
watchTasks = [];
if (bundle) watchTasks.push('bundle');
if (minify) watchTasks.push('minify');
-}
-else {
+} else {
// by default, do both bundling and minifying
watchTasks = ['bundle', 'minify'];
}
// The watch task (to automatically rebuild when the source code changes)
-gulp.task('watch', watchTasks, function () {
+gulp.task('watch', watchTasks, function() {
gulp.watch(['index.js', 'lib/**/*'], watchTasks);
});
diff --git a/package.json b/package.json
index e1d285ee..7e936343 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"main": "./dist/vis.js",
"scripts": {
"test": "mocha --compilers js:babel-core/register",
+ "gemini": "gulp gemini-tests",
"build": "gulp",
"lint": "eslint lib",
"watch": "gulp watch",
@@ -41,15 +42,16 @@
"babel-core": "^6.6.5",
"babel-eslint": "^7.1.1",
"babel-loader": "^6.2.4",
- "babel-polyfill": "^6.22.0",
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
"babel-plugin-transform-es3-property-literals": "^6.8.0",
"babel-plugin-transform-runtime": "^6.22.0",
+ "babel-polyfill": "^6.22.0",
"babel-preset-es2015": "^6.6.0",
"babel-runtime": "^6.22.0",
"babelify": "^7.3.0",
"clean-css": "^4.0.2",
"eslint": "^3.15.0",
+ "gemini": "^5.0.0-alpha.3",
"gulp": "^3.9.1",
"gulp-clean-css": "^2.3.2",
"gulp-concat": "^2.6.1",
@@ -59,6 +61,7 @@
"jsdom-global": "^2.1.1",
"mocha": "^3.2.0",
"mocha-jsdom": "^1.1.0",
+ "opn": "^5.1.0",
"rimraf": "^2.5.4",
"uglify-js": "^2.7.5",
"uuid": "^3.0.1",
diff --git a/test/gemini/demo1.js b/test/gemini/demo1.js
new file mode 100644
index 00000000..5c715add
--- /dev/null
+++ b/test/gemini/demo1.js
@@ -0,0 +1,8 @@
+gemini.suite('yandex-search', function(suite) {
+ suite.setUrl('/')
+ .setCaptureElements('.main-table')
+ .capture('plain')
+ .capture('with text', function(actions, find) {
+ actions.sendKeys(find('.input__control'), 'hello gemini');
+ });
+});
diff --git a/test/gemini/demo2.js b/test/gemini/demo2.js
new file mode 100644
index 00000000..656070ec
--- /dev/null
+++ b/test/gemini/demo2.js
@@ -0,0 +1,5 @@
+gemini.suite('yandex-icons', function(suite) {
+ suite.setUrl('/foo')
+ .setCaptureElements('.tabs')
+ .capture('plain');
+});