Cypress with Type script

https://testautomationu.applitools.com/cypress-with-typescript/chapter1.html

//user can define own types on type script
type EvenNumbers = 2|4|6

export  const add = (a: number , b :EvenNumbers) =>{
    return a+b
}

add(21,2,4)


install type script Globerly

> npm i typescript -g

To view all different commands and flages 

> tsc 

it will convert TS file to JS 


Cypress Auto-completion use following

/// <reference types="cypress"/>

but when using type script by adding default type script config file 

references will help auto load 

tsconfig.ts 

{
    "compilerOptions": {
      "target": "es5",
      "lib": ["es5", "dom"],
      "types": ["cypress", "node"],
      "resolveJsonModule": true
    },
    "include": ["**/*.ts"]
}



custom commands on Type script and add header description


export { } // use to remove higlight errors
declare global {
    namespace Cypress{
        interface Chainable{
            findLocator2(input:string):
            Chainable<any>
        }

    }
}

Cypress.Commands.add('findLocator2',(locator:string)=>{    
           
Cypress.log({
    displayName:'getByPlaceholder',
    message:locator,
    consoleProps(){
        return {
            selector: locator
        }
    }
})

    return cy.get('${locator}',{
        log:false
    })
})

Add example 

/**
             * Get a dom value path
             * @param input locator value
             * @example <---------------
             * //this command
             * cy.findlocator('your locator path')
             */

function is not use anymore/ obsalete

@deprecated


When having error on XPath with type script


1 add this in your tsconfig.json file.

"cypress-xpath"

{
    "compilerOptions": {
      "target": "es5",
      "lib": ["es5", "dom"],
      "types": ["cypress", "node","cypress-xpath"], //<------------
      "resolveJsonModule": true
    },
    "include": ["**/*.ts", "support/commands.js"]
}

import "cypress-xpath" to command.ts file

import "cypress-xpath"



Set specific value without using constent values

Create file helper\Typing\Placeholders.ts

export type Attribute = 'attr'|'src'|'title'

export type Placeholder = 'placeholder'|'test'


create method using above mention types

InvokeAttrShould(locator:string,attr:Placeholders,placeholder:string,contain:string,message:string){
       
        cy.get(locator).invoke(attr,placeholder)
        .should(contain,message)              
    }

when use it,only able to type given values

I.InvokeAttrShould(partner.IPAddresses,
'attr', //<----------------------------
'placeholder',// <---------------------------
        'contain',
        partnerData.IPAddressesPlaceholder)      



Change class to models

https://stackoverflow.com/questions/32805559/typescript-es6-import-module-file-is-not-a-module-error

// test.js - exporting es6

export module App {
  export class SomeClass {
    getName(): string {
      return 'name';
    }
  }
  export class OtherClass {
    getName(): string {
      return 'name';
    }
  }
}

And now we can import it with these thre ways:

import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";

And we can consume imported stuff like this:

var a1: app1.App.SomeClass  = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();

var b1: app2.App.SomeClass  = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();

var c1: App.SomeClass  = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();

and call the method to see it in action:

console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())


change json imports to 


old (it will not read data inside)

const HomePage = require('../../../pageObjects/Home/homePages.json')

New 

import HomePage = require('../../../pageObjects/Home/homePages.json');


















 

Delete table before install

 DECLARE @TestId AS VARCHAR(100)='58311'

  delete FROM [SanaPlatform].[dbo].[WebStoreAzureSettings]  where [WebStoreId] =@TestId

  delete FROM [SanaPlatform].[dbo].[WebstoreDatabaseUsers]  where [WebStoreId] =@TestId

  delete FROM [SanaPlatform].[dbo].[WebStoreDomains]  where [WebStoreId] =@TestId

  delete  FROM [SanaPlatform].[dbo].[WebstoreEmailSettings]  where [WebStoreId] =@TestId

  delete FROM [SanaPlatform].[dbo].[WebstoreInfoEmailSentLogs]  where [WebStoreId] =@TestId

  delete  FROM [SanaPlatform].[dbo].[WebstoreLifeCycles]   where [WebStoreId] =@TestId

  delete FROM [SanaPlatform].[dbo].[WebstoreMaintenanceEmails]  where [WebStoreId] =@TestId

  delete FROM [SanaPlatform].[dbo].[WebStores] where [WebStoreId] = @TestId

Webstore with azure side settings | Create project Underermined

Webstore with azure side settings

 [dbo].[WebStoreAzureSettings]


can get webstore installation profile id created by cluster wise

make Hard coded Partner called: "Sana team undetermined"

Create projects called: Undetermined 

If the partner is not sure then create as undetermined.

Partner ==> WebstoreInstallationProfiles ==> WebStoreAzureSettings




 









Access restrictions

 

To view the "Access restrictions" menu need to enable 





if the site is limited change the settings 
https://support.sana-commerce.com/Content/Sana-User-Guide/Basics/Webstore-Access-Mode.htm












clear all check boxes

 $(':checkbox').prop('checked', false)

plaformuser Remote destopt connection

 20.86.84.78

.\plaformuser

abcABC@12345


ingress

 *ingress

cus dom

multi store 

custom header 


When need to restart stagin (webapp)




cypress errors

 



Remove from the command.js file 


const cypress = require("cypress");

import { cli } from 'cypress';


When the page not loading due to cash


module.exports = defineConfig({
  ...
    ...
    numTestsKeptInMemory:0
  },
});


When install 

cypress-mochawesome-reporter




use force to install it as follows
 

npm i --save-dev cypress-mochawesome-reporter --force




Json Not working with typescript 

to fix add the following to tsconfig file 


{
  "compilerOptions": {
   ...
    "resolveJsonModule": true, <-------------------
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true, // use json import
  },
 
}

----------------------------------------------------------------------------------------------------------------

if got the following error >  install again

npm install cypress --save-dev












Find resources to delete / unused resource .

1. Application Insights


https://portal.azure.com/#home

select all subscription




01 Application Insight
copy name from the unused resource,

go to Azure Home > applicationinsight.



02. Database

Azure > Sql servers 
search staging 
search databases 


select DB item and delete.


03. Database backup blob and 
04. Content backup blob




Microsoft Azure storage explore.
go to the correct region. Search from here. 

1 Database backup 
2 Content backup 



05. DNS



06. Cluster
not yet 

piwik 
dynatrace monitor should 
dns
webstore domain should remove 
uptime XX
 




Sana Release package

 go to: https://portal.azure.com/#home



Search versions which not in sana side


Run script on power shell (Run as admin)

[string]$Name = "Sana Commerce Cloud 1.47.43.0";

[string]$ImageTag = "1.47.43.0";

[string]$ProjectId = "DC360555-DA8D-4C95-965E-365B1DE3A6E2";

[string]$ImageRepository = "sanaacrweu01.azurecr.io/scc-dev";

[string]$MigrationsZip = "D:\Migration\Migrations.zip";

[string]$Database = "D:\Migration\Database.zip";

[string]$MigrationToolkitVersion = "0.1.9.0";cls

[string]$DeploymentUri = "https://staging-platform.sana-commerce.com/";

[string]$PlatformUser = "i.jayakody@sana-commerce.com";

[string]$PlatformPass = "abcABC@1234";

[string]$ReleaseType = "hotfix";//patch

[string]$IsSDK = "false";


$migrations = [Convert]::ToBase64String([IO.File]::ReadAllBytes($MigrationsZip))

$database = [Convert]::ToBase64String([IO.File]::ReadAllBytes($Database))


$credentialsJson = @"

{

  "Email": "$platformUser",

  "Password": "$platformPass"

}

"@

$authResponse = Invoke-RestMethod -Uri "$deploymentUri/api/v1/accounts/authentication-token" -Method Post -Body $credentialsJson -ContentType "application/json"


$releasePackageJson = @"

{

  "Name": "$Name",

  "Version": "$imageTag",

  "ImageRepository": "$imageRepository",

  "ImageTag": "$imageTag",

  "InstallationScriptsZip": "$database",

  "MigrationToolkitVersion": "$MigrationToolkitVersion",

  "MigrationsZip": "$migrations",

  "ProjectId": "$ProjectId",

  "ReleaseType":"$ReleaseType",

  "IsSDK":$IsSDK

}

"@


$token = $authResponse.AccessToken

$headers = @{Authorization = "Bearer $token"}


Invoke-RestMethod -Uri "$deploymentUri/api/v1/release-package" -Method Post -Body $releasePackageJson -Headers $headers -ContentType "application/json"


--------------------------------------------------------------------------------------------------------------------

Recently added release package


need to ask, is the latest path is changed to 



https://portal.azure.com/#view/Microsoft_Azure_ContainerRegistries/RepositoryBlade/id/%2Fsubscriptions%2F2c8d3fc1-89cc-40e5-9e7f-1e2fdadcccc9%2FresourceGroups%2FRG-ACR-WEU%2Fproviders%2FMicrosoft.ContainerRegistry%2Fregistries%2Fsanadevelopmentacrweu01/repository/scc-dev





Cypress Basic to Advance

1. Create file Platform_Login.cy.js under Cypress\e2e\Platform\Login

describe('My First Test', () => {
    it('Visits Sana framwork', () => {
      cy.visit('https://staging-platform.sana-commerce.com/')      
 
      // that the value has been updated
     cy.get('#Email')
    .type('s.kodagoda@sana-commerce.com')
    .should('have.value', 's.kodagoda@sana-commerce.com')
   cy.get('#Password')
    .type('xxxxxxxxx@123')
    .should('have.value', 'xxxxxxxxx@123')
   
  cy.get('[type=submit]').click()
  cy.title().should('eq','Sana Platform')
   
    })
  })


2. Create Folder Cypress\testbase\testbase.js


class testbase{
 
    Fill(locator, text)
{
        //Get element by by Html element       
        cy.get(locator).type(text)  
    }

    Click(locator)
{
        cy.get(locator).click()
    }
 

}
module.exports = new testbase();

Make it advance with Xpath Addon


class testbase{
 
    Fill(locator, text)
{
        //Get element by by Xpath
        if(locator.includes('//')) {
            cy.xpath(locator).type(text)
        }
        //Get element by by Html element
        else {
            cy.get(locator).type(text)            
        }  
    }

    Click(locator)
{
        cy.get(locator).click()
    }
}
module.exports = new testbase();


Update Login.cy.js

const I = require('../../../testbase/testbase')

describe('Test Login', () => {
  beforeEach(()=>{
    cy.visit('https://staging-platform.sana-commerce.com/')
})


  it('Test Login',()=>{                
      I.Fill('#Email','s.kodagoda@sana-commerce.com')        

  })


  })


Add file cypress/ pageObjects/loginPages.json

{
    "Login": {
      "LoginData": {
        "Email": "#Email",
        "Password": "#Password",
        "CaptureCheckBox": "div.recaptcha-checkbox-border",
        "LoginButton": "button.btn-block"
      }
    }
}

import page objects to the login page 

...
const PlatformLogin = require('../../../pageObjects/loginPages.json')

describe('Test Login', () =>
...

  it('Test Login',()=>{                
      I.Fill(PlatformLogin.Login.LoginData.Email,'s.kodagoda@sana-commerce.com')  
  })
...
 

import test data to login page 

const I = require('../../../testbase/testbase')
const PlatformLogin = require('../../../pageObjects/loginPages.json')
const TestData = require('../../../testdata/platformLoginData.json')

describe('Test Login', () => {
  beforeEach(()=>{
    cy.visit('https://staging-platform.sana-commerce.com/')
})


  it('Test Login',()=>{                
      I.Fill(PlatformLogin.Login.LoginData.Email,TestData.Login.LoginUserData.Email)        

  })

  })


Finaly Add Password and Login button

const I = require('../../../testbase/testbase')
const PlatformLogin = require('../../../pageObjects/loginPages.json')
const TestData = require('../../../testdata/platformLoginData.json')

describe('Test Login', () => {
  beforeEach(()=>{
    cy.visit('https://staging-platform.sana-commerce.com/')
})


  it('Test Login',()=>{                
      I.Fill(PlatformLogin.Login.LoginData.Email,TestData.Login.LoginUserData.Email)        
      I.Fill(PlatformLogin.Login.LoginData.Password,TestData.Login.LoginUserData.Password)
      I.Click(PlatformLogin.Login.LoginData.LoginButton)
  })

  })


Stage 2

How to use Fixtures (Load set of data located in a file)

copy Platform_Login.cy.js to the fixtures folder


Fat arrow function'=>'  with normal function
this keyword not working on Fat arrow function

()=>   ==  function()
(data)) => ==  function(data)


describe('Test Login', () => {
...
//Not working
     cy.fixture('PlatformLogins').then((data) => {
         this.data = data;         
         cy.log('e-mail: ',this.data.Email)
               
      })


PlatformLogins.json
  {
    "Name":"should login to Home page",
    "Email":"s.kodagoda@sana-commerce.com",
    "Password":"1smStandard@123",
    "Expected":"Invalid login attempt"    
  }
   

it('Test Fixture,Get All Platform Emails.', function() {
   
      cy.fixture('PlatformLogins').then(function(TestDataList) {
        this.TestDataList = TestDataList;

        cy.log('e-mail: ',this.TestDataList.Email)         
    })     
  })

add another 'it'

  it('Test Fixture,Login Platform Emails.', function() {
   
    cy.fixture('PlatformLogins').then(function(TestDataList) {
      this.TestDataList = TestDataList;

      cy.log('e-mail: ',this.TestDataList.Email)
   
     I.Fill(PlatformLogin.Login.LoginData.Email,this.TestDataList.Email)        
     I.Fill(PlatformLogin.Login.LoginData.Password,this.TestDataList.Password)
     I.Click(PlatformLogin.Login.LoginData.LoginButton)          
  })


Test with Multiple data

update PlatformLogins.json

[
    {
    "Name":"should login to Home page",
    "Email":"apitestuser@sana-commerce.com",
    "Password":"xxxxxxxxxxxxxxxxxxxxxx",
    "Expected":"Invalid login attempt"
    },
    {
        "Name":"should display incorrect user name message",
        "Email":"xxx@sana-commerce.com",
        "Password":"xxx@123",
        "Expected":"Invalid login attempt"
    },
    {
        "Name":"should display incorrect password message",
        "Email":"xxx@sana-commerce.com",
        "Password":"xxx@123",
        "Expected":"Invalid login attempt"
    }
]

add foreach to a basic method

it('Test Fixture,Get All Platform Emails.', function() {
   
      cy.fixture('PlatformLogins').then(function(TestDataList) {
        this.TestDataList = TestDataList;

        this.TestDataList.forEach(testLogin => {
           cy.log('e-mail: ',testLogin.Email)
         })
        //cy.log('e-mail: ',this.TestDataList.Email)              
    })      
  })

add foreach to a Login

 it('Test Fixture,Login Platform Emails.', function() {
   
    cy.fixture('PlatformLogins').then(function(TestDataList) {
      this.TestDataList = TestDataList;

      this.TestDataList.forEach(testLogin => {                
        I.Fill(PlatformLogin.Login.LoginData.Email,testLogin.Email)        
        I.Fill(PlatformLogin.Login.LoginData.Password,testLogin.Password)
        I.Click(PlatformLogin.Login.LoginData.LoginButton)
      })        
    })    
  })


create a folder fixture/testData and copy  PlatformLogins.json



get file 

const TestDataList = require('../../../fixtures/testData/PlatformLogins.json')

  // ~ ForEach Example ~

  TestDataList.forEach(testLogin =>{

    it(testLogin.Name,() =>{
      I.Fill(PlatformLogin.Login.LoginData.Email,testLogin.Email)        
      I.Fill(PlatformLogin.Login.LoginData.Password,testLogin.Password)
      I.Click(PlatformLogin.Login.LoginData.LoginButton)
               

      if(testLogin.Name=="should login to Home page")
      {
         
      }
      else{
        I.SeeText(PlatformLogin.Login.LoginData.ErrorMessage,testLogin.Expected)    
        //cy.get(PlatformLogin.Login.LoginData.ErrorMessage).should('have.text',testLogin.Expected)
      }

    })
  })


Cypress Hooks 

Create file e2e/hooks.cy.js

/*
Order

1. before --> Execute once, as soon as the first Test Script is executed
2 beforeEach > Executed Before Each TS is executed
3. testExecution
4. afterEach >  Executed After Each TS is executed
5. after > Execute once, as soon as the Last Test Script is executed
*/

describe('',function(){

    before(function(){
        cy.log('Before')
    })
    beforeEach(function(){
        cy.log('Before each')
    })


    it('TC #1',function(){
        console.log('TC #1')
    })
    it('TC #2',function(){
        console.log('TC #2')
    })
    it('TC #3',function(){
        console.log('TC #3')
    })
    it('TC #4',function(){
        console.log('TC #4')
    })

   
    afterEach(function(){
        cy.log('After each')
    })

    after(function(){
        cy.log('After')
    })
})


Commands 

video 12 (https://www.youtube.com/watch?v=66bEpdatEYQ&list=PLYDwWPRvXB8-8LG2hZv25HO6C3w_vezZb&index=12&ab_channel=JoanMedia)


support/commands.js

Cypress.Commands.add('typeLogin',(username,password)=>{
    cy.get('#Email').type(username)
    cy.get('#Password').type(password)
})


const PlatformLogin = require('../pageObjects/loginPages.json')

Cypress.Commands.add('typeLogin',(username,password)=>{
    cy.get(PlatformLogin.Login.LoginData.Email).type(username)
    cy.get(PlatformLogin.Login.LoginData.Password).type(password)
})


create file 

e2e/Commands/commadsTest.cy.js

typeLogin can call anywhere  ,support/e2e.js  jas already import 

//This is e2e.js
//Import commands.js using ES2015 syntax:
import './commands'


describe('Command example',function(){
    beforeEach(function(){
        cy.visit('https://staging-platform.sana-commerce.com/')      

       cy.typeLogin('s.kodagoda@sana-commerce.com','abc')

    })

    it('Visits Sana framwork', () => {      
      cy.log('test comands')  
cy.url().should('include', 'Home')   
      })
})


Add logout 

command.js

Cypress.Commands.add('logout',()=>{    
    cy.get('.image > .img-circle').click()
    cy.get("#logoutForm > a").click()
})

commandTest.cy.js

it('Test Login', () => {      
      cy.log('test comands')  
cy.url().should('include', 'Home')    
      })

      afterEach(function(){ <--------
        cy.logout()
      })


Move Logout details to pageobject

cypress/pageObject/homePages.json

{
    "Home": {
      "HeaderPanel": {
        "PlatformLogo": ".logo",
        "ProfileImage" : ".image > .img-circle",
        "ButonLogout" : "#logoutForm > a"
      }
    },
    "LeftPanelMenu": {
       "UserAccounts" : "//p[contains(text(),'User accounts')]",
       "ManageRolesMenuItem" : "//p[contains(text(),'Manage roles')]"
   
    }
}


update loginHelper

export function I_Login(){
  ...
}

export function I_Logout(){
    I.Click(PlaformHome.Home.HeaderPanel.ProfileImage)
    I.Click(PlaformHome.Home.HeaderPanel.ButonLogout)  
}


How Login/Logout use in Sana platform Code.

use separate Hepler class cypress/helpers/loginHelper.js

const I = require('../testbase/testbase')
const PlatformTestData = require('../fixtures/platformLoginData.json')
const PlaformLogin = require('../pageObjects/loginPages.json')

export function I_Login(){
    I.Fill(PlaformLogin.Login.LoginData.Email,PlatformTestData.Login.LoginUserData.Email)
    I.Fill(PlaformLogin.Login.LoginData.Password,PlatformTestData.Login.LoginUserData.Password)
    I.Click(PlaformLogin.Login.LoginData.LoginButton)
}


create file e2e/Platfrom/Home/PlatformHome.cy.js

import { I_Login } from "../../../helpers/loginHelper";

describe('Home page Testing', () => {
    it('get url from config file', () => {     
      cy.visit('https://staging-platform.sana-commerce.com/')          
 
      I_Login()
   
    })
  })


How to add site URL on config file 

update cypress.config.js 

Old versions have cypress.json but with new version its use on cypress.config.js 

...
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    baseUrl: 'https://staging-platform.sana-commerce.com/'
  },
});

PlatformHome.cy.js

import { I_Login } from "../../../helpers/loginHelper";

describe('Home page Testing', () => {
    it('get url from config file', () => {
      cy.visit(Cypress.config().baseUrl)
     
      I_Login()   
    })
  })


Move Visit method to testbase.js 

    SeeText(locator, expectedText)
    {  ... }

    Open(url="")
    {
        cy.visit(url);
    }


PlatformHome.cy.js

import { I_Login } from "../../../helpers/loginHelper";
const I = require('../../../testbase/testbase')

describe('Home page Testing', () => {
    it('get url from config file', () => {

      I.Open(Cypress.config().baseUrl)
      I_Login()   
    })
  })


If only import a function inside a file separately.

import { I_Login } from "../../../helpers/loginHelper";

if want to access all methods/functions

const LoginHelper = require('../../../helpers/loginHelper')


PlatformHome.cy.js

const LoginHelper = require('../../../helpers/loginHelper')
const I = require('../../../testbase/testbase')

describe('Home page Testing', () => {
    it('get url from config file', () => {
 
      I.Open(Cypress.config().baseUrl)
      LoginHelper.I_Login()      
      LoginHelper.I_Logout()   
    })
  })


add before each and after each.

const LoginHelper = require('../../../helpers/loginHelper')
const I = require('../../../testbase/testbase')

describe('Home page Testing', () => {
    beforeEach(function(){
        I.Open(Cypress.config().baseUrl)
        LoginHelper.I_Login()
    })

    it('get url from config file', () => {
     
      cy.log('test comands')
cy.url().should('include', 'Home')
   
    })

    afterEach(function(){
        LoginHelper.I_Logout()
      })

  })

Log and URL assertion move to testbase.js

Open(url="")
    {
       ...
    }
    Log(text){
        cy.log(text)
    }
    UrlInclude(pageName){
        cy.url().should('include', pageName)
    }
SeeInTitle(pageTitle)
    {
        cy.title().should('contain', pageTitle);
    }

PlatformHome.cy.js

describe('Home page Testing', () => {
    beforeEach(function(){
      ..
    })

    it('get url from config file', () => {     
      I.Log('test comands')
      I.UrlInclude('Home'
I.SeeInTitle('Sana Platform'
    })
...  
  })


Login with Session

need to add following to cypress.config.js

experimentalSessionAndOrigin : true


const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      // implement node event listeners here
    },
    baseUrl: 'https://staging-platform.sana-commerce.com/',
    experimentalSessionAndOrigin : true  
  },
});


following example can pass session name as single string  or multiple strings.

describe('Login test with session', () => {
   
    beforeEach(()=>{
       
        cy.session('user',()=>{ //<-------- single string
            LoginHelper.I_Login()
           })
    })


multiple strings.

cy.session(['user','password',.....],()=>{


update login helper

export function I_Login(){
   
    I.Open(Cypress.config().baseUrl) <--------
    I.Fill(PlaformLogin.Login.LoginData.Email,PlatformTestData.Login.LoginUserData.Email)
    I.Fill(PlaformLogin.Login.LoginData.Password,PlatformTestData.Login.LoginUserData.Password)
    I.Click(PlaformLogin.Login.LoginData.LoginButton)
}

export function I_LoginWithSession(){
    cy.session('user',()=>{        
        I.Open('/')
        I.Fill(PlatformLogin.Login.LoginData.Email,PlatformTestData.Login.LoginUserData.Email)
        I.Fill(PlatformLogin.Login.LoginData.Password,PlatformTestData.Login.LoginUserData.Password)
        I.Click(PlatformLogin.Login.LoginData.LoginButton)
    })
}


When use commands.js 

const PlatformLogin = require('../pageObjects/loginPages.json')
const PlatformTestData = require('../fixtures/platformLoginData.json')
const I = require('../testbase/testbase')

...

Cypress.Commands.add('LoginWithSession',()=>{
    cy.session('user',()=>{              
        I.Open('/')
        I.Fill(PlatformLogin.Login.LoginData.Email,PlatformTestData.Login.LoginUserData.Email)
        I.Fill(PlatformLogin.Login.LoginData.Password,PlatformTestData.Login.LoginUserData.Password)
        I.Click(PlatformLogin.Login.LoginData.LoginButton)
    })

})


before and after use session Time reduce


always need to call the base URL 

it('Validate main 4 items',()=>{
      I.Open('/') <---------------
           
      cy.get('.container-fluid .row').find('.col-lg-3').should('have.length', 4)


    })

use unique id for session 

Install uuid

npm install uuid

command.js

import { v4 as uuidv4 } from 'uuid';
const userUuid = "user_"+uuidv4(); //<-----

Cypress.Commands.add('LoginWithSession',()=>{
   
    cy.session(userUuid,()=>{ //<-----
    //cy.session('user',()=>{    
        //cy.log("uuid :"+userUuid)
        I.Open('/')
        I.Fill(PlatformLogin.Login.LoginData.Email,PlatformTestData.Login.LoginUserData.Email)
        I.Fill(PlatformLogin.Login.LoginData.Password,PlatformTestData.Login.LoginUserData.Password)
        I.Click(PlatformLogin.Login.LoginData.LoginButton)
    })

})


Update homePages.json 

"Home": {
...
"DashBoardCards":[
        {  
          "Name":"ActiveTrial",    
          "Button":":nth-child(1) > .small-box > .small-box-footer",
          "RedirectUrl":"Webstore/AllWebstores?category=Active%20Trials",
          "DetailPageRedioButton":"#activeTrialsCheckBox"
        },
        {
          "Name":"InProduction",  
          "Button":":nth-child(2) > .small-box > .small-box-footer",
          "RedirectUrl":"Webstore/AllWebstores?category=In%20Production",
          "DetailPageRedioButton":"#inProductionCheckBox"
        },      
        {
          "Name":"ExpiredTrials",  
          "Button":":nth-child(3) > .small-box > .small-box-footer",
          "RedirectUrl":"Webstore/AllWebstores?category=Expired%20Trials",
          "DetailPageRedioButton":"#extendTrialsCheckBox"
        },    
        {
          "Name":"ExpiredTrials",  
          "Button":":nth-child(4) > .small-box > .small-box-footer",
          "RedirectUrl":"#",
          "DetailPageRedioButton":"#archivedCheckBox"
        }
      ]


PlatformHome.cy.js

 ...

it('Redirect to Active trails',()=>{
      I.Open('/')
      I.Click(HomePage.DashBoard.DashBoardCards.ActiveTrial.Name)      
      I.UrlInclude(HomePage.DashBoard.DashBoardCards.ActiveTrial.RedirectUrl)
      //I.Wait(1000)      
      cy.get(HomePage.DashBoard.DashBoardCards.ActiveTrial.DetailPageRedioButton).should('be.checked').and('have.value', 'Active Trials')
     
    }) 

    it('Redirect to In production',()=>{
      I.Open('/')
      I.Click(HomePage.DashBoard.DashBoardCards.InProduction.Name)      
      I.UrlInclude(HomePage.DashBoard.DashBoardCards.InProduction.RedirectUrl)
      cy.get('#inProductionCheckBox').should('be.checked').and('have.value', 'In Production')
     
    })
...


To reduce code, we can write above as following

PlatformHome.cy.js


//use for each
    const dashBoardCards = HomePage.DashBoardCards;
   
    dashBoardCards.forEach(dashBoardCard =>{
      it("Testing Dash board card: " + dashBoardCard.Name,() =>{
       
         I.Open('/')
         I.Click(dashBoardCard.Button)      
         I.UrlInclude(dashBoardCard.RedirectUrl)
         I.Wait(1000)      
          cy.get(dashBoardCard.DetailPageRedioButton)
          .should('be.checked')
          .and('have.value', dashBoardCard.RedioButtonValue)        
      })
    })


Home Header buttons 

homePage.json

"Home": {
      ...
        "TopMenu":[{
          "Name":"Manage",
          "Button":".main-header > .navbar-nav > :nth-child(1) > .nav-link",
          "RedirectUrl":"/Webstore/AllWebstores"
         
        },
        {
          "Name":"New",          
          "Button":".navbar-nav > :nth-child(2) > .nav-link",
          "RedirectUrl":"/Webstore/Install"          
        },
        {
          "Name":"Create",
          "Button":".navbar-nav > :nth-child(3) > .nav-link",
          "RedirectUrl":"/User/CreateUser"          
        },
        {
          "Name":"Help",
          "Button":".navbar-nav > :nth-child(4) > .nav-link",
          "RedirectUrl":"/help"          
        }
      ]
      }
    },


How to write one by one 

//Home Header buttons : Manage
    it('Redirect to Expired Trials',()=>{
      I.Open('/')
       //const ss = HomePage.Home.HeaderPanel.TopMenu;

      I.Click(HomePage.Home.HeaderPanel.TopMenu[0].Button)        
      I.UrlInclude(HomePage.Home.HeaderPanel.TopMenu[0].RedirectUrl)
      cy.get(HomePage.Home.HeaderPanel.TopMenu[0].DetailPageRedioButton).should('be.checked')
      .and('have.value', 'All')
    })  


Home page Left Panel menu Testing 

NewInstallation.json

 },
    "LeftPanelMenu": {
       "MenuButton":"cy.get('.main-nav-wrapper > .navbar-nav > .nav-item > .nav-link')",
       "LeftPanelMenuItems":[
        {
          "Name":"Installations",
          "Button":".active",
          "MenuItems" :[{
            "Name":"New installation",
            "Button":".menu-open > .nav > :nth-child(1) > .nav-link",
            "RedirectUrl":"/Webstore/Install"          
          },
          {
            "Name":"All installations",
            "Button":".menu-open > .nav > :nth-child(2) > .nav-link",
            "RedirectUrl":"/Webstore/AllWebstores"
          }
        ]
         
        },
        {
          "Name":"Approvals",
          "Button":"#approvalMenu > [href='#']",
          "MenuItems" :[{
            "Name":"New installation",
            "Button":"#approvalMenu > .nav > :nth-child(1) > .nav-link",
            "RedirectUrl":"/Approval/WebstoreInstallationRequests"          
            }
          ]          
        }
       ]        
    },


Platform.cy.js

 // Left Panel Menu --------------------------------------------------

    const leftPanelMenuItems = HomePage.LeftPanelMenu.LeftPanelMenuItems;
    leftPanelMenuItems.forEach(leftPanelMenuItem =>{
     
      const subMenuItems = leftPanelMenuItem.MenuItems;
      subMenuItems.forEach(subMenuItem =>{

        it("Left Panel: " + leftPanelMenuItem.Name+" : " + subMenuItem.Name,() =>{
       
          I.Open('/')
          I.Click(leftPanelMenuItem.Button)    
          I.Click(subMenuItem.Button)      
          I.UrlInclude(subMenuItem.RedirectUrl)          
        })
      })



Add Snippet for VS Code

Ctrl +shift +p

type "snippet" 

select javascript it will open javascript .js file 

"Console log":{
        "prefix":"cl",
        "body":"console.log($1);",
        "description": "Console Log"
    }

When yo type cl it will create the code for you

 console.log();

https://snippet-generator.app/


    },
    "IT": {
        "prefix": "it",
        "body": [
          "it(${1:functionName},()=>{",
          "})"
        ],
        "description": "IT"
      }

after use

     it(functionName,()=>{
     })


cypress screenshort.

it(userData.Title,() =>{
    I.Fill(PlatformLogin.Login.LoginData.Email,userData.Email)        
    I.Fill(PlatformLogin.Login.LoginData.Password,userData.Password
   
cy.screenshot(); //<----

  })

it will create a new folder.



How  to retry 

...
    baseUrl: 'https://staging-platform.sana-commerce.com/',
    experimentalSessionAndOrigin : true  ,
    retries:2 // 1 + 2 extra times
  },
});


Configure Dashboard 

Cypress.config.js

login and add project id 

module.exports = defineConfig({
....
    retries:3,
    projectId: "w81smh"
  },
});


cypress dashboard

https://www.youtube.com/watch?v=ydTviHjpxh0&ab_channel=JoanMedia




past on :cypress.config.js

  e2e: {
    .....
    retries:3,
    projectId: "w81smh" // <-------
  },


npx cypress run --record --key da6d9485-4c46-49f9-b5e3-c4a9926889ef


Note: To locate the project-id and record-key of your Cypress project, navigate to Project > Project Settings in the Cypress dashboard. You can find the project-id and record-key under the General and



cypress-axe

https://www.npmjs.com/package/cypress-axe


npm install --save-dev cypress-axe

package.json  record adds automatically.

 "cypress-axe": "^1.0.0",

add e2e.js

import 'cypress-axe'

accessbility.cy.js


describe('Testing', () => {    
     
    it("How to test accesbility",()=>{
        cy.visit("https://staging-platform.sana-commerce.com/")
        cy.injectAxe() // staart the injector / scan the site
        cy.checkA11y()

    })

 })


improve 

https://www.youtube.com/watch?v=TaBhwaOy1XI&ab_channel=Sparkbox



Add Emoji 

click extension > type emoji

install 




File >Preferance > Settings 

User Tab> Extention > :emojisense > Edit in Settings .json


paset the flowing test inside emojisense.languages

"javascript.validate.enable": false, "abap": true, "bat": true, "bibtex": true, "clojure": true, "coffeescript": true, "c": true, "cpp": true, "csharp": true, "css": true, "diff": true, "dockerfile": true, "fsharp": true, "git-commit": true, "git-rebase": true, "go": true, "groovy": true, "handlebars": true, "html": true, "ini": true, "java": true, "javascript": true, "javascriptreact": true, "json": true, "jsonc": true, "latex": true, "less": true, "lua": true, "makefile": true, "markdown": true, "objective-c": true, "objective-cpp": true, "perl6": true, "php": true, "powershell": true, "jade": true, "python": true, "r": true, "razor": true, "ruby": true, "rust": true, "scss": true, "sass": true, "shaderlab": true, "shellscript": true, "sql": true, "swift": true, "typescript": true, "typescriptreact": true, "tex": true, "vb": true, "xml": true, "xsl": true, "yaml": true }

full doc looks like as following 

{
    "diffEditor.codeLens": true,
    "javascript.referencesCodeLens.enabled": true,
    "javascript.referencesCodeLens.showOnAllFunctions": true,
    "typescript.implementationsCodeLens.enabled": true,
    "typescript.referencesCodeLens.enabled": true,
    "typescript.referencesCodeLens.showOnAllFunctions": true,
   
    "emojisense.languages": {

        "markdown": true,
        "plaintext": {
            "markupCompletionsEnabled": true,
            "emojiDecoratorsEnabled": true
        },
        "scminput": true,
        "git-commit": true,

        "javascript.validate.enable": false,
        "abap": true,
        "bat": true,
        "bibtex": true,
        "clojure": true,
        "coffeescript": true,
        "c": true,
        "cpp": true,
        "csharp": true,
        "css": true,
        "diff": true,
        "dockerfile": true,
        "fsharp": true,
        "git-commit": true,
        "git-rebase": true,
        "go": true,
        "groovy": true,
        "handlebars": true,
        "html": true,
        "ini": true,
        "java": true,
        "javascript": true,
        "javascriptreact": true,
        "json": true,
        "jsonc": true,
        "latex": true,
        "less": true,
        "lua": true,
        "makefile": true,
        "markdown": true,
        "objective-c": true,
        "objective-cpp": true,
        "perl6": true,
        "php": true,
        "powershell": true,
        "jade": true,
        "python": true,
        "r": true,
        "razor": true,
        "ruby": true,
        "rust": true,
        "scss": true,
        "sass": true,
        "shaderlab": true,
        "shellscript": true,
        "sql": true,
        "swift": true,
        "typescript": true,
        "typescriptreact": true,
        "tex": true,
        "vb": true,
        "xml": true,
        "xsl": true,
        "yaml": true        
    }
}


Then Type -- > :Circle 

/*
  //checkPageA11y
  //https://www.youtube.com/watch?v=TaBhwaOy1XI&ab_channel=Sparkbox
  Cypress.Commands.add('checkPageA11y', (path) => {
     
      cy.visit(path)
      cy.injectAxe() // staart the injector / scan the site
      cy.checkA11y(null,null,callback)
    })
   

function callback(violations){

    violations.forEach(violation =>{
        const nodes = Cypress.$(violation.nodes.map(node=> node.target).join(','))

        Cypress.log({
            name:`${severityIndicators[violation.impact]} A11y`,
            consoleProps:()=> violation,
            $el:nodes,
            message:`[${violation.help}](${violation.helpUrl})`
        })

        violation.nodes.forEach(({target}) => {
            Cypress.log({
                name: '🔧',
                consoleProps:() => violation,
                $el: Cypress.$(target.join(',')),
                message:target
            })
        })


    })
}
const severityIndicators={
    minor: 'Minor ⚪',
    moderate: 'Moderate 🟡',
    serious: 'Serious 🟠',
    critical: 'Critical 🔴'
   
}

*/


accesbilityhelper.js

  //checkPageA11y
  //https://www.youtube.com/watch?v=TaBhwaOy1XI&ab_channel=Sparkbox

const severityIndicators={
    minor: 'Minor ⚪',
    moderate: 'Moderate 🟡',
    serious: 'Serious 🟠',
    critical: 'Critical 🔴'
}

function callback(violations){

    violations.forEach(violation =>{
        const nodes = Cypress.$(violation.nodes.map(node=> node.target).join(','))

        Cypress.log({
            name:`${severityIndicators[violation.impact]} A11y`,
            consoleProps:()=> violation,
            $el:nodes,
            message:`[${violation.help}](${violation.helpUrl})`
        })

        violation.nodes.forEach(({target}) => {
            Cypress.log({
                name: '🔧',
                consoleProps:() => violation,
                $el: Cypress.$(target.join(',')),
                message:target
            })
        })


    })
}

export function I_CheckPageAccesbility_A11y(path){
     
    cy.visit(path)
    cy.injectAxe() // start the injector / scan the site
    cy.checkA11y(null,null,callback)
}


Type script 

install 

npm install --save-dev typescript


it will add record in package.json 

"devDependencies": {
    "cypress": "^10.3.0",
    "cypress-axe": "^1.0.0",
    "cypress-xpath": "^2.0.0",
    "typescript": "^4.8.4" <------------------------
  },

Configure tsconfig.json

We recommend creating a tsconfig.json inside your cypress folder with the following configuration:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es5", "dom"],
    "types": ["cypress", "node"]
  },
  "include": ["**/*.ts"]
}


Invoke 

can use the Jquery function inside cypress

cy.get('#IPAddresses').invoke('attr', 'placeholder').should('contain', 'The partne..')


cypress file upload

npm install --save-dev cypress-file-upload


find 

      // cy.get(".card-columns").find('.card').each(($card) => {
        cy.get(overviewPage.Overview.CardsPath).each(($card) => {            
            count += 1
        }).then(() =>{
            expect(count, 'count').to.equal(8)
            cy.get(overviewPage.Overview.CardsPath).its('length').should('eq', count)
        })



testautomationu.applitools.com



from: https://testautomationu.applitools.com/cypress-tutorial/chapter3.html

Whether Cypress will watch and restart tests on test file changes.

watchForFileChanges:false //it will control restart tests on test file changes.


Delay an element 

?delay-new-todo=5000'

cy.get('.new-todo',{timeout:6000})

it("functionName",()=>{
    cy.visit('http://todomvc-app-for-testing.surge.sh/?delay-new-todo=5000')
   
    cy.get('.new-todo',{timeout:6000}).type("Clean room{enter}")
})


How to get the length of the collection and return 

it.only("Multiple assertions",()=>{
     
        cy.get("table[id='partnerGrid'] thead > tr >th")
         .then(item =>{
               
            const listingCount = item.length
            //const listingCount = Cypress.$(item).length;
            cy.log("List count "+listingCount)             
           
         })              
       
     })

simple way 

 cy.get("#partnerGrid>thead>tr>th").should('have.length','4')


How to throw an error 

 throw new Error("sdsdsd")


Expect text 

expect(item[1]).to.contain.text('Sana ERP partner')


https://testautomationu.applitools.com/advanced-cypress-tutorial/chapter4.html

When the element is hidden 

 cy.get("location").invoke('show')
        .click()

        // add css class
 cy.get("location")
        .invoke('addClass','NewClassName')
       
        //hover change event trigger
 cy.get("location")
        .trigger('mouseover')


environment variables 

export function FillGenerateName(locator,name){
    const randomName = Name_Alpha_Numeric()
    name = name+randomName
    cy.get(locator).type(name)
   
    // Create Environment variables
    Cypress.env('GenerateWebStoreName', name)  //<-------------
    return name;
}


  let webStoreName = Cypress.env('GenerateWebStoreName')


Get name from and assign to the variable 

 cy.get(allWebStoresTable.LatestWebStoreNamePath)
         .then(item =>{
                 webStoreName = item.text()
                 I.Log("Site Name inside: "+ webStoreName)


covert API response to json

convert json to string
const webstores =JSON.stringify(res.body)
cy.log(webstores)


Cypress Auto-completion use following

/// <reference types="cypress"/>


---------------------------------------------------------------------------

Install Cypress Grep for tagging

npm install -g cypress-grep

 npm i -D @cypress/grep --force 





it will auto-update package,json and package-lock.json



update e2e.ts 

// Import commands.js using ES2015 syntax:
import './commands'
import './commands/commands'
import 'cypress-axe'
import 'cypress-file-upload'

// Alternatively you can use CommonJS syntax:
// require('./commands')
require('cypress-xpath');

 const registerCypressGrep = require('@cypress/grep')
 registerCypressGrep()

// if you want to use the "import" keyword
// note: `./index.d.ts` currently extends the global Cypress types and
// does not define `registerCypressGrep` so the import path is directly
// pointed to the `support.js` file
import registerCypressGrep from '@cypress/grep/src/support'
registerCypressGrep()


add command so no need to call all the cy.ts pages 




describe('🥇 Field Verification',{tags:'@smoke'}, () => {

npx cypress run --env grepTags=@smoke


npx cypress run --headed --env grepTags=@smoke --record --key 56dcf788-902a-42f3-ba5f-93b05694a61d

How to run Head mode 

 npx cypress run --headed


Add reporting to Cypress 

install  cypress-mochawesome-reporter

follow this : https://www.npmjs.com/package/cypress-mochawesome-reporter

1 install cypress-mochawesome-reporter

npm i --save-dev cypress-mochawesome-reporter 

--force 

use following to ignore warnings 
npm i --save-dev cypress-mochawesome-reporter --force

it will update package.json 

 "cypress-mochawesome-reporter": "^3.5.0", <--------------------


Change cypress reporter & setup hooks

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  reporter: 'cypress-mochawesome-reporter',
  e2e: {
    setupNodeEvents(on, config) {
      require('cypress-mochawesome-reporter/plugin')(on); 
    },
  },
});
Add to cypress/support/e2e.js
import 'cypress-mochawesome-reporter/register';

it will create a folder as 
~\cypress\reports\html



-----------------------------------------------------------------------------------------------------------
Change base URL on comandline 

package.json

"scripts": {
    ...
   
"cypress:open4": "cypress open --config baseUrl=http://www.google.com/",  
    
   
  },

execute as follows,
npm run cypress:open4






Visual testing 

https://applitools.com/
https://applitools.com/tutorials/quickstart/web/cypress#applitools-eyes-cypress-sdl

to install 

npm install @applitools/eyes-cypress

and 
npx eyes-setup

it will add new dependency to 'package.json'





Cypress Chrome recorder

https://www.youtube.com/watch?v=-RJuZrq-wOk&t=194s&ab_channel=Cypress.io

1. install the chrome browser extension.
2. record the process and save 
3. When saving as JSON  you need to convert JSON to a cypress file 
    to do that we are required to install following 

npm install --location=global  @cypress/chrome-recorder

(disable the Global protector)

4. after installation, convert the file. 
create output folder path (cypress/tests/ui )

npx @cypress/chrome-recorder D:/Recordingcy.json -o=cypress/tests/ui


or use Export via extension  > cypress Test


 

Cypress Cross-origin testing



install "@cypress/webpack-preprocessor"

cypress.config.ts  set following 
chromeWebSecurity:false
experimentalOriginDependencies :true,


To prevent button click exceptions use 


Cypress.on('uncaught:exception', (err, runnable) => {
    // returning false here prevents Cypress from failing the test
    return false
  })





Cypress API plugging

https://www.youtube.com/watch?v=kENXELkT4O4&ab_channel=LambdaTest



npm install --save-dev @bahmutov/cy-api
command.ts
import '@bahmutov/cy-api'

update tsconfig.json

"types": ["cypress", "node","cypress-xpath","cypress-axe","@bahmutov/cy-api"],

output



Cy spok

npm i -D cy-spok
its uses to make expected body  items value generic 


chai-json-schema


npm install chai-json-schema

e2e.ts

chai.use(require('chai-json-schema'));



Json Not working with typescript 

to fix add folowing to tsconfig file 


{
  "compilerOptions": {
   ...
    "resolveJsonModule": true, <-------------------
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true, // use json import
  },
 
}



cypress API New with new update

https://www.npmjs.com/package/cypress-plugin-api


npm i cypress-plugin-api
it will add new dependent 

"dependencies": {
    "chai-json-schema": "^1.5.1",
    "cypress-plugin-api": "^2.11.0" <--------------------------------
  }