From dd721484d8dbcd7394b0eebef3fa8199d6d522f9 Mon Sep 17 00:00:00 2001 From: Trevor Nelms Date: Tue, 13 Dec 2022 15:50:43 -0700 Subject: [PATCH] SBVT-699: Update Cypress to use CDN for scripts (#19) * SBVT-699: Gets the CDNs as soon as the plugin file is loaded * SBVT-699: Remove the folder with all the hard coded minified files * SBVT-699: Renaming function * SBVT-699: removing junk * SBVT-699: removing async for the return function Co-authored-by: trevor.nelms --- commands.js | 7 +- plugin.js | 167 +++++++++++-------- sbvt-browser-toolkit/dist/dom-capture.min.js | 1 - sbvt-browser-toolkit/dist/freeze-page.min.js | 1 - sbvt-browser-toolkit/dist/user-agent.min.js | 1 - sbvt-browser-toolkit/index.js | 11 -- 6 files changed, 105 insertions(+), 83 deletions(-) delete mode 100644 sbvt-browser-toolkit/dist/dom-capture.min.js delete mode 100644 sbvt-browser-toolkit/dist/freeze-page.min.js delete mode 100644 sbvt-browser-toolkit/dist/user-agent.min.js delete mode 100644 sbvt-browser-toolkit/index.js diff --git a/commands.js b/commands.js index bd1f9c3..73df6f9 100644 --- a/commands.js +++ b/commands.js @@ -19,7 +19,7 @@ let toolkitScripts; let deviceInfoResponse; Cypress.Commands.add('sbvtCapture', { prevSubject: 'optional' }, (element, name, options) => { - if (!toolkitScripts) cy.task('loadScripts').then((scripts) => toolkitScripts = scripts) //load the scripts from the toolkit + if (!toolkitScripts) cy.task('getToolkit').then((scripts) => toolkitScripts = scripts); imageName = (name) ? name : (function(){throw new Error("sbvtCapture name cannot be null, please try sbvtCapture('Example name')")})(); //check for file name and then assign to global let imageType = (options && options.capture) ? options.capture : imageType; //pass through options.capture if provided @@ -29,10 +29,9 @@ Cypress.Commands.add('sbvtCapture', { prevSubject: 'optional' }, (element, name, cy.task('logger', {type: 'trace', message: `Beginning sbvtCapture('${name}')`}); if (element) cy.task('logger', {type: 'trace', message: 'This is chained and there is an "element" value'}); - cy.window() .then((win) => { - userAgentData = win.eval(toolkitScripts.userAgentScript) + userAgentData = win.eval(toolkitScripts.userAgent) return cy.task('postTestRunId', userAgentData).then((taskData) => { vtConfFile = taskData; //grab visualTest.config.js data cy.request({ @@ -177,7 +176,7 @@ let picFileFormat = () => { let domCapture = () => { cy.window() .then((win) => { - dom = win.eval(toolkitScripts.captureDomScript) + dom = win.eval(toolkitScripts.domCapture) }); }; let getImageById = () => { diff --git a/plugin.js b/plugin.js index 3267440..86636fd 100644 --- a/plugin.js +++ b/plugin.js @@ -1,7 +1,6 @@ const axios = require('axios').default; const { v4: uuidv4 } = require('uuid'); const fs = require("fs"); -const toolkitScripts = require('./sbvt-browser-toolkit/index'); const package_json = require('./package.json'); const cwd = process.cwd(); const path = require("path"); @@ -14,78 +13,116 @@ logger.level = 'warn' ;// warn will be the default level for debug logs //set debug flag on visualTest.config.js file by including: PINO_LOG_LEVEL: 'trace' //options are [trace, debug, info, warn, error, fatal] in that order -let config = {}; - -//TODO work on this file if cypress is not a devDependency, and a regular it throws errors... - let usersCypress; try { const packageFile = fs.readFileSync(path.resolve(path.dirname(require.resolve('cypress', {paths: [cwd]})), 'package.json')) usersCypress = JSON.parse(packageFile.toString()); if (!usersCypress.version) { - usersCypress.version = "10.0.0.failure" // #TODO improve this if cypress folder isnt found (when the folder isnt a devDependency) + usersCypress.version = "10.0.0.failure" logger.warn('failed to find cypress assuming it is v10+') } } catch (err) { logger.warn("catch") - usersCypress.version = "10.0.0" // #TODO improve this if cypress folder isnt found + usersCypress.version = "10.0.0" console.log(err) logger.warn(err.message) } +let configFile = (() => { + try { + let config = {} + const fileName = 'visualTest.config.js'; + const fullPath = `${process.cwd()}/${fileName}`; + if (fs.existsSync(fullPath)) { + logger.trace(fileName + ' has been found'); + config = {...require(fullPath)}; //write the VT config file into config object + + function getCdnUrl() { + return config.apiHost === 'https://api.dev.visualtest.io' ? "https://cdn.dev.visualtest.io/browser-toolkit" + : config.apiHost === 'https://api.int.visualtest.io' ? "https://cdn.int.visualtest.io/browser-toolkit" + : "https://cdn.visualtest.io/browser-toolkit"; + } + config.cdnUrl = getCdnUrl(); + return config; + } else { + config.fail = true; + logger.fatal('The path ' + fullPath + ' was not found'); + return config; + } + } catch (e) { + console.log(e) + }})(); + +let getDomCapture = (async () => { + try { + const domCapture = await axios.get(`${configFile.cdnUrl}/dom-capture.min.js`) + return domCapture.data + } catch (error) { + configFile.fail = true; + logger.fatal(`Error with grabbing getDomCapture: %o`, error.message); + } +})(); + +let getUserAgent = (async () => { + try { + const domCapture = await axios.get(`${configFile.cdnUrl}/user-agent.min.js`) + return domCapture.data + } catch (error) { + configFile.fail = true; + logger.fatal(`Error with grabbing getUserAgent: %o`, error.message); + } +})(); + +let domToolKit = null +Promise.all([getDomCapture, getUserAgent]).then((values) => { + const data = {} + data.domCapture = values[0] + data.userAgent = values[1] + domToolKit = data +}); + function makeGlobalRunHooks() { return { 'task': { async postTestRunId (userAgent) { //cy.task('postTestRunId') to run this code - if (!config.testRunId && !config.fail) {//all this only needs to run once - const fileName = 'visualTest.config.js'; + if (!configFile.testRunId && !configFile.fail) {//all this only needs to run once const sessionId = uuidv4(); - const fullPath = `${process.cwd()}/${fileName}`; - if (fs.existsSync(fullPath)) { - logger.trace(fileName + ' has been found'); - config = {...require(fullPath)}; //write the VT config file into config object - } else { - config.fail = true; - logger.fatal('The path ' + fullPath + ' was not found'); - return config; - } - - if (config.PINO_LOG_LEVEL) { - logger.level = config.PINO_LOG_LEVEL //overwrite if the user includes a pino flag in VTconf - } else if (config.log) { - logger.level = config.log + if (configFile.PINO_LOG_LEVEL) { + logger.level = configFile.PINO_LOG_LEVEL //overwrite if the user includes a pino flag in VTconf + } else if (configFile.log) { + logger.level = configFile.log } - if (!config.projectToken) { //check to make sure user added a projectToken - config.fail = true; + if (!configFile.projectToken) { //check to make sure user added a projectToken + configFile.fail = true; logger.fatal(`Please add **module.exports = { projectToken: 'PROJECT_TOKEN' }** to your visualTest.config.js file`); - return config; + return configFile; } - if (config.projectToken.includes("_")) { //check to make sure the user changed it from the default - config.fail = true; + if (configFile.projectToken.includes("_")) { //check to make sure the user changed it from the default + configFile.fail = true; logger.fatal(`Please insert your actual projectToken`); - return config; + return configFile; } - if (!config.projectToken.split('/')[1]) { //check to make sure user added the auth part(~second-half) of projectToken - config.fail = true; + if (!configFile.projectToken.split('/')[1]) { //check to make sure user added the auth part(~second-half) of projectToken + configFile.fail = true; logger.fatal(`Please add your full projectToken for example -> ** projectToken: 'xxxxxxxx/xxxxxxxxxxxx' **`); - return config; + return configFile; } - logger.trace('config.projectToken: ' + config.projectToken); - config.projectId = config.projectToken.split('/')[0]; //take the first ~half to get the projectId - logger.trace('config.projectId: ' + config.projectId); + logger.trace('config.projectToken: ' + configFile.projectToken); + configFile.projectId = configFile.projectToken.split('/')[0]; //take the first ~half to get the projectId + logger.trace('config.projectId: ' + configFile.projectId); - axios.defaults.headers.common['Authorization'] = `Bearer ${config.projectToken}`; + axios.defaults.headers.common['Authorization'] = `Bearer ${configFile.projectToken}`; logger.trace(`axios.defaults.headers.common['Authorization']: ` + axios.defaults.headers.common['Authorization']); - config.sessionId = sessionId; - logger.trace('config.sessionId: ' + config.sessionId); + configFile.sessionId = sessionId; + logger.trace('config.sessionId: ' + configFile.sessionId); - if (!config.testRunName) { //if testRunName not defined---testRunName will be the sessionId + if (!configFile.testRunName) { //if testRunName not defined---use device / browser let osPrettyName; if (userAgent.osName === 'macos') { osPrettyName = 'macOS'; @@ -97,40 +134,40 @@ function makeGlobalRunHooks() { const browserPrettyName = str.charAt(0).toUpperCase() + str.slice(1); const browserMajorVersion = userAgent.browserVersion.split('.'); - config.testRunName = `${osPrettyName} ${userAgent.osVersion} / ${browserPrettyName} ${browserMajorVersion[0]}`; + configFile.testRunName = `${osPrettyName} ${userAgent.osVersion} / ${browserPrettyName} ${browserMajorVersion[0]}`; } - logger.trace('config.testRunName: ' + config.testRunName); + logger.trace('config.testRunName: ' + configFile.testRunName); - if (config.apiHost) { + if (configFile.apiHost) { logger.debug('Found config.apiHost') - config.url = config.apiHost - logger.warn('overwritten URL is: ' + config.url); - } else{ - config.url = 'https://api.visualtest.io'; - logger.trace('URL is: ' + config.url); + configFile.url = configFile.apiHost + logger.warn('overwritten URL is: ' + configFile.url); + } else { + configFile.url = 'https://api.visualtest.io'; + logger.trace('URL is: ' + configFile.url); } - config.websiteUrl = config.url.replace('api', 'app'); - logger.trace('config.websiteUrl: ' + config.websiteUrl); + configFile.websiteUrl = configFile.url.replace('api', 'app'); + logger.trace('config.websiteUrl: ' + configFile.websiteUrl); - config.cypressVersion = usersCypress.version + configFile.cypressVersion = usersCypress.version try { - const postResponse = await axios.post(`${config.url}/api/v1/projects/${config.projectId}/testruns`, { - testRunName: config.testRunName, + const postResponse = await axios.post(`${configFile.url}/api/v1/projects/${configFile.projectId}/testruns`, { + testRunName: configFile.testRunName, sdk: 'cypress', sdkVersion: `${package_json.version}/c${usersCypress.version}` }) - config.testRunId = postResponse.data.testRunId; - logger.debug('config.testRunId: ' + config.testRunId); + configFile.testRunId = postResponse.data.testRunId; + logger.debug('config.testRunId: ' + configFile.testRunId); } catch (error) { - config.fail = true; + configFile.fail = true; logger.fatal(`Error with creating testRun: %o`, error.message); logger.trace(`Full error with creating testRun: %o`, error); - return config; + return configFile; } - config.fail = false; //no errors in generating testRunId + configFile.fail = false; //no errors in generating testRunId logger.trace('—————————————————Successfully created a testRunId—————————————————'); } - return config; + return configFile; }, async logger ({type, message}) { //this task is for printing logs to node console from the custom command type === 'fatal' ? logger.fatal(message) : @@ -142,19 +179,19 @@ function makeGlobalRunHooks() { logger.warn('error with the logger task') return null }, - async loadScripts () { - return toolkitScripts + getToolkit () { + return domToolKit; } }, 'after:run': async () => { - if (config.fail === false) { + if (configFile.fail === false) { try { - const imageResponse = await axios.get(`${config.url}/api/v1/projects/${config.projectId}/testruns/${config.testRunId}/images`); + const imageResponse = await axios.get(`${configFile.url}/api/v1/projects/${configFile.projectId}/testruns/${configFile.testRunId}/images`); const imageCount = imageResponse.data.page.totalItems; process.stdout.write(`View your ${imageCount} ${(imageCount === 1 ? 'capture' : 'captures')} here: `); - console.log(chalk.blue(`${config.websiteUrl}/projects/${config.projectId}/testruns/${config.testRunId}/comparisons`)); + console.log(chalk.blue(`${configFile.websiteUrl}/projects/${configFile.projectId}/testruns/${configFile.testRunId}/comparisons`)); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); @@ -164,7 +201,7 @@ function makeGlobalRunHooks() { let comparisonTotal = 0; for (let i = 0; comparisonTotal !== imageCount && i < 15; i++) { if (i > 0) await sleep(250); //don't wait the first iteration - comparisonResponse = await axios.get(`${config.url}/api/v1/projects/${config.projectId}/testruns/${config.testRunId}?expand=comparison-totals`); + comparisonResponse = await axios.get(`${configFile.url}/api/v1/projects/${configFile.projectId}/testruns/${configFile.testRunId}?expand=comparison-totals`); comparisonTotal = comparisonResponse.data.comparisons.total; } let comparisonResult = comparisonResponse.data.comparisons; @@ -177,7 +214,7 @@ function makeGlobalRunHooks() { } catch (error) { console.error(error); } - } else if (config.fail === true) { + } else if (configFile.fail === true) { logger.fatal('There were issues with VisualTest. Check above logs.'); } else { console.log('There were no VisualTest captures taken.'); diff --git a/sbvt-browser-toolkit/dist/dom-capture.min.js b/sbvt-browser-toolkit/dist/dom-capture.min.js deleted file mode 100644 index 72eaf7a..0000000 --- a/sbvt-browser-toolkit/dist/dom-capture.min.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{try{let e={error:false,viewport:{left:null,top:null,width:null,height:null},fullpage:{width:null,height:null},devicePixelRatio:null,dom:[]};let t={elements:0,objects:0};let r=["script","param"];let o=["noscript","option"];let n=e.dom;let i=e=>{if(e.nodeType==Node.ELEMENT_NODE)if(r.indexOf(e.tagName.toLowerCase())>-1)return NodeFilter.FILTER_REJECT;else if(o.indexOf(e.tagName.toLowerCase())>-1)return NodeFilter.FILTER_SKIP;else return NodeFilter.FILTER_ACCEPT;else if(e.nodeType==Node.TEXT_NODE)if(e.textContent.trim())return NodeFilter.FILTER_ACCEPT};let l=document.createTreeWalker(document.documentElement,NodeFilter.SHOW_ELEMENT+NodeFilter.SHOW_TEXT,i,false);let d=e=>{try{attributes=e.getAttributeNames();return attributes}catch(e){return"failed"}};let u=(e,t)=>{try{return e.getAttribute(t)}catch(e){return"failed"}};let c=(e,t)=>{try{if(window.getComputedStyle)return document.defaultView.getComputedStyle(e,null).getPropertyValue(t);else if(e.currentStyle)return e.currentStyle[t];else return""}catch(e){return"failed"}};let a=e=>{var t=e.getBoundingClientRect(),r=document.body,o=document.documentElement,n=window.pageYOffset||o.scrollTop||r.scrollTop,i=window.pageXOffset||o.scrollLeft||r.scrollLeft,l=o.clientTop||r.clientTop||0,d=o.clientLeft||r.clientLeft||0,u=t.top+n-l,c=t.left+i-d,a=t.right-t.left,s=t.bottom-t.top;return{top:Math.round(u),left:Math.round(c),width:Math.round(a),height:Math.round(s)}};let s=e=>{String.prototype.correctWhiteSpace=function(){return this.replace(/\s+/g," ")};return"string"==typeof e?e.correctWhiteSpace().trim():""};let h=()=>{var e={left:window.scrollX,top:window.scrollY,width:window.innerWidth,height:window.innerHeight};return e};let f=()=>({width:Math.max(window.document.body.offsetWidth,window.document.body.scrollWidth,window.document.documentElement.offsetWidth,window.document.documentElement.scrollWidth),height:Math.max(window.document.body.offsetHeight,window.document.body.scrollHeight,window.document.documentElement.offsetHeight,window.document.documentElement.scrollHeight)});let m=()=>{t.elements++;let e=n;let r=null;if(l.currentNode.nodeType==Node.ELEMENT_NODE){var o=a(l.currentNode);var i={top:o.top,left:o.left,width:o.width,height:o.height};var h={};var f={};var g=d(l.currentNode);g.forEach((e=>{f[e]=u(l.currentNode,e)}));var w=["background-repeat","background-origin","background-position","background-color","background-image","background-size","border-width","border-color","border-style","color","display","font-size","font-weight","line-height","margin","opacity","overflow","padding","visibility","text-align","position","border-radius","z-index"];w.forEach((e=>{h[e]=c(l.currentNode,e)}));r=e.push({domId:t.elements,tagname:s(l.currentNode.tagName),box:i,styles:h,attrs:f,children:[]})}else if(l.currentNode.nodeType==Node.TEXT_NODE)r=e.push({domId:t.elements,tagname:"text",value:l.currentNode.textContent.trim()});if(l.firstChild()){n=e[r-1].children;m()}if(l.nextSibling()){n=e;m()}else{l.parentNode();return}};e.viewport=h();e.fullpage=f();e.devicePixelRatio=window.devicePixelRatio;m();error=0==e.dom.length;return JSON.stringify(e)}catch(e){void 0;return JSON.stringify({error:true,message:e.message})}})(); \ No newline at end of file diff --git a/sbvt-browser-toolkit/dist/freeze-page.min.js b/sbvt-browser-toolkit/dist/freeze-page.min.js deleted file mode 100644 index c6015f1..0000000 --- a/sbvt-browser-toolkit/dist/freeze-page.min.js +++ /dev/null @@ -1 +0,0 @@ -window.vt={isGif:function(e){var t=/^(?!data:).*\.gif/i.test(e.src);void 0;return t},freezeGif:function(e){var t=document.createElement("canvas");var a=t.width=e.width;var i=t.height=e.height;t.getContext("2d").drawImage(e,0,0,a,i);try{e.src=t.toDataURL("image/gif")}catch(a){for(var r=0,n;n=e.attributes[r];r++)t.setAttribute(n.name,n.value);e.parentNode.replaceChild(t,e)}},freezeGifs:function(){[].slice.apply(document.images).filter(vt.isGif).map(vt.freezeGif)},setAnimationDurationZero:function(){var e=document.getElementsByTagName("*");for(var t=0;t0){a.style.animationDuration="0s";void 0}}catch(e){}}},freezejQueryFx:function(){if(window.jQuery)jQuery.fx.off=true},getElementName:function(e){return e.tagName+(e.id?"#"+e.id:e.className?"."+e.className:"")},freeze:function(){vt.freezeGifs();vt.setAnimationDurationZero();vt.freezejQueryFx()}}; \ No newline at end of file diff --git a/sbvt-browser-toolkit/dist/user-agent.min.js b/sbvt-browser-toolkit/dist/user-agent.min.js deleted file mode 100644 index 9cb924e..0000000 --- a/sbvt-browser-toolkit/dist/user-agent.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(){function e(e,n){this.name=e;this.pattern=n}e.prototype.match=function e(n){var r=n.match(this.pattern);if(null===r)return null;var i={name:this.name,version:null};if("undefined"!==typeof r[1])i.version=r[1].replace(/_/g,".");return i};var n=[new e("Firefox",/Firefox\/([0-9.]+)/),new e("Edge",/Edg[AeiOS]{0,3}\/([0-9.]+)/),new e("Opera",/OPR\/([0-9.]+)/),new e("SamsungInternet",/SamsungBrowser\/([0-9.]+)/),new e("UCBrowser",/UCBrowser\/([0-9.]+)/),new e("Chrome",/Chrome\/([0-9.]+)/),new e("Safari",/Version\/([0-9.]+)(?: .*)? Safari\//),new e("InternetExplorer",/(?:MSIE |IEMobile\/|Trident\/.*rv:)([0-9.]+)/)];var r=[new e("Windows",/Windows NT ([0-9.]+)/),new e("Windows Phone",/Windows Phone ([0-9.]+)/),new e("MacOS",/OS X ([0-9._]+)/),new e("iOS",/iPhone OS ([0-9_.]+)/),new e("iPadOS",/iPad.+?OS ([0-9_,]+)/),new e("ChromeOS",/CrOS [^ ]+ ([0-9.]+)/),new e("Android",/(?:Android|Adr) ([0-9.]+)/),new e("BlackBerry",/BlackBerry|BB10/),new e("webOS",/webOS\/([0-9.]+)/),new e("Linux",/Linux/)];var i={osName:"",osVersion:"",browserName:"",browserVersion:"",screenWidth:"",screenHeight:"",devicePixelRatio:""};function o(e){var o="string"===typeof e?e:window.navigator.userAgent,t=false,a,s;for(s=0;swindow.screen.height?"landscape":"portrait";i.devicePixelRatio=window.devicePixelRatio||1;return t}o();return i})(); \ No newline at end of file diff --git a/sbvt-browser-toolkit/index.js b/sbvt-browser-toolkit/index.js deleted file mode 100644 index cbe247f..0000000 --- a/sbvt-browser-toolkit/index.js +++ /dev/null @@ -1,11 +0,0 @@ -const fs = require('fs'); - -const getScript = filename => { - return fs.readFileSync(__dirname + `/dist/${filename}.js`,{encoding:'utf8', flag:'r'}); -}; - -const captureDomScript = getScript('dom-capture.min'); -const freezePageScript = getScript('freeze-page.min'); -const userAgentScript = getScript('user-agent.min'); - -module.exports = { captureDomScript, freezePageScript, userAgentScript }; \ No newline at end of file