33

I have an editor page. When I add any content and click the "Save" button my URL will change, adding a random id in the URL. I want to check if my ID's are changing every time when I click the "Save button".

I save the URL result in variable and want to check it, I do it like this:

const currentURL = cy.url();
cy.get('.editor-toolbar-actions-save').click();
cy.url().should('not.eq', currentURL);

But my currentURL variable's type is not string:

expected http://localhost:8080/editor/37b44d4d-48b7-4d19-b3de-56b38fc9f951 to not equal { Object (chainerId, firstCall) }

How I can use my variable?

Narine Poghosyan
  • 853
  • 3
  • 16
  • 26

6 Answers6

42

tl;dr

Cypress commands are asynchronous, you have to use then to work with their yields.

cy.url().then(url => {
  cy.get('.editor-toolbar-actions-save').click();
  cy.url().should('not.eq', url);
});

Explanation

A similar question was asked on GitHub, and the official document on aliases explains this phenomenon in great detail:

You cannot assign or work with the return values of any Cypress command. Commands are enqueued and run asynchronously.

The solution is shown too:

To access what each Cypress command yields you use .then().

cy.get('button').then(($btn) => {
  // $btn is the object that the previous
  // command yielded us
})

It is also a good idea to check out the core concepts docs's section on asynchronicity.

Alasdair McLeay
  • 2,572
  • 4
  • 27
  • 50
totymedli
  • 29,531
  • 22
  • 131
  • 165
  • Thanks my dear, its work, after adding cy.wait(2000) on my code – Narine Poghosyan Jun 27 '19 at 12:56
  • 4
    Note that Cypress *is not* normal await/async JavaScript, but uses its own callback mechanism which is chainable. Just to make sure there is no confusion when you say "Cypress is async". – Mikko Ohtamaa Aug 11 '20 at 15:09
  • @totymedli I have a problem of: ``` it.only("LoginFunctionality", function () { cy.visit("hardCoddedLinkIsHere"); cy.get(".chakra-stack > .css-1n94901").click(); cy.contains("Login withxxxx").click(); cy.url().then((url) => { href = url; cy.log("href ", href); }); }); ''' it.only("Verification of login", function () { cy.visit(href); cy.url().should("include", "something"); //assertion of that we are in this url }); ``` Error: cy.visit() must be called with a url or an options object containin a url as its 1st arg – Luna Oct 22 '21 at 12:51
  • @totymedli kindly asking you to check – Luna Oct 22 '21 at 12:55
  • @Luna You need to ask this in a new question with the full code. Its hard to help you without details. Check what you pass to `cy.visit()`. Maybe in your second function `href` has an unexpected value. The variable is not declared in that function so it has to come from an upper scope. Make sure it has a proper value. – totymedli Oct 23 '21 at 03:43
  • @totymedli I opened new question. https://stackoverflow.com/questions/69676503/cy-visit-must-be-called-with-a-url-or-an-options-object-containing-a-url-as-it?noredirect=1#comment123158718_69676503 – Luna Oct 24 '21 at 12:53
6

These commands return a chainable type, not primitive values like strings, so assigning them to variables will require further action to 'extract' the string.

In order to get the url string, you need to do

cy.url().then(urlString => //do whatever)
Vangelisz Ketipisz
  • 897
  • 1
  • 7
  • 10
2

I have been having the same issue and so far most consistent method has been to save the URL to file and read it from file when you need to access it again:

//store the url into a file so that we can read it again elsewhere
cy.url().then(url => {
    const saveLocation = `cypress/results/data/${Cypress.spec.name}.location.txt`
    cy.writeFile(saveLocation, getUrl)
})

//elsewhere read the file and do thing with it
cy.readFile(`cypress/results/data/${Cypress.spec.name}.location.txt`).then((url) => {
    cy.log(`returning back to editor ${url}`)
    cy.visit(url)
})
Max Barrass
  • 2,776
  • 1
  • 19
  • 10
1

Try this:

describe("Test Suite", () => {
  let savedUrl;
  beforeEach(() => {
    cy.visit("https://duckduckgo.com/");
    cy.url().then(($url) => {
      savedUrl = $url;
    });
  });

  it("Assert that theURL after the search doens't equal the URL before.", () => {
    cy.get("#search_form_input_homepage").type("duck");
    cy.get("#search_button_homepage").click();

    // Check if this URL "https://duckduckgo.com/?q=duck&t=h_&ia=web"
    // doesn't equal the saved URL "https://duckduckgo.com/"
    cy.url().should("not.eq", savedUrl);
  });
});
0

Refer below code snippet, Here you can get the current URL and store it in a variable, do print via cy.log()

context('Get Current URL', () => {
 
    it('Get current url and print', () => {
        cy.visit('https://docs.cypress.io/api/commands/url')
    
        cy.url().then(url => {
            const getUrl = url
            cy.log('Current URL is : '+getUrl)
        })
    })
})
Prabhu Mohan
  • 119
  • 2
0

@Max thanks this helped to get some ideas on different versions. The way I did it is:

  1. Create a .json file in your fixtures folder (name it whatever you want).

  2. On the new .json file, only add: { } brackets and leave the rest blank. The function will self populate that .json file.

  3. Create a new function on the commands page to easily call it on your test.

  4. It would probably be best to create two functions, 1 function to write url or the sliced piece of the url, and the another function to call it so you can use it. A. Example of 1st method, this method cuts the id off of the URL and stores it on the .json file:

    Cypress.Commands.add('writeToJSON', (nameOfJSONSlicedSection) =>
     { 
         cy.url().then(urlID =>
             {
                 let urlBit = urlID.slice(urlID.indexOf('s/') + 2, urlID.indexOf('/edit'))
                 cy.writeFile('cypress/fixtures/XYZ.json', {name: nameOfJSONSlicedSection, id: urlBit}) /*{ }<-- these will populate the json file with name: xxxxx and id:xxxxx, you can changes those to whatever meets your requirements. using .slice() to take a section of the url. I needed the id that is on the url, so I cut only that section out and put it on the json file.*/
             })
     })
    

B. 2nd example function of calling it to be used. This function is to type in the id that is on the url into a search box, to find the item I require on a different it() block.

Cypress.Commands.add('readJSONFile', (storedJSONFile) =>
{
    cy.readFile('cypress/fixtures/XYZ.json').its('id').then((urlSetter) => {
        cy.log(storedJSONFile, 'returning ID: ' + urlSetter)
        //Search for Story
        cy.get('Search text box').should('be.visible').type(urlSetter, {delay: 75}) 
    })
})
/*here I use a .then() and hold the "id" with "urlSetter", then I type it in the search box to find it by the id that is in the URL. Also note that using ".its()" you can call any part section you require, example: .its('name') or .its('id') */

I hope this helps!