admin管理员组文章数量:1200819
I am very new to Cypress, so go easy.
We have a React application that, on initial load, presents a "Login with Spotify" button.
The button constructs an auth URL according to the Spotify PKCE authorization flow, and directly sets window.location.href
to that constructed URL:
//spotifyAuth.js
export const getAuthorizationURL = async () => {
logMessage(`Getting authorization URL...`);
const authUrl = new URL(";);
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Save the codeVerifier in IndexedDB
await setCachedEntry('auth', codeVerifier, 'spotify_code_verifier');
logMessage(`Code verifier: ${codeVerifier}`);
const state = generateState();
const params = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
scope: 'user-library-read',
state: state,
redirect_uri: REDIRECT_URI,
code_challenge_method: 'S256',
code_challenge: codeChallenge,
});
authUrl.search = new URLSearchParams(params).toString();
const authorizationURL = authUrl.toString();
logMessage(`Authorization URL: ${authorizationURL}`);
return { authorizationURL };
};
export const redirectToAuthorizationUrl = async () => {
logMessage(`Redirecting to authorization URL...`);
window.location.href = (await getAuthorizationURL()).authorizationURL;
}
To start off with, We are just trying to test that clicking that button redirects to the correct URL.
We read that a single Cypress test cannot span across multiple origins. So, instead of allowing the redirect and attempting to read the new URL, we tried to stub the redirect, block it and read what the application is trying to set window.location.href
to:
//login.cy.js
describe('Login Functionality', () => {
describe('When you click Login to Spotify', () => {
it('constructs the Spotify auth url', () => {
cy.window().then(window => {
cy.stub(window.location, 'href').as('redirect')
})
cy.visit('/');
cy.get('.login-button').click();
cy.get('@redirect').should('be.calledWithMatch', `accounts.spotify/authorize`)
});
});
});
That resulted an an AssertionError
: Timed out retrying after 4000ms: expected redirect to have been called with arguments matching "accounts.spotify/authorize", but it was never called
.
We then read that you cannot stub window.location.href
directly like that. So, we tried to change the application code to set the auth URL in a function and stub that function instead:
//spotifyAuth.js
export const redirectToAuthorizationUrl = async () => {
logMessage(`Redirecting to authorization URL...`);
const authUrlToNavigateTo = (await getAuthorizationURL()).authorizationURL;
window.redirectToSpotifyAuth(authUrlToNavigateTo);
}
window.redirectToSpotifyAuth = function (authUrl) {
window.location.href = authUrl;
};
//login.cy.js
describe('Spotify Login Flow', () => {
it('should construct correct Spotify auth URL', () => {
// Stub the redirect function
cy.visit('http://localhost:3000/', {
onBeforeLoad(window) {
window.redirectToSpotifyAuth = () => {};
cy.stub(window, 'redirectToSpotifyAuth').as('redirect');
},
});
cy.get('.login-button').click();
// Check that redirectToSpotifyAuth was called with the correct URL
cy.get('@redirect').should('be.calledWithMatch', `accounts.spotify/authorize`);
});
});
This resulted in the same AssertionError
.
What are we doing wrong?
I am very new to Cypress, so go easy.
We have a React application that, on initial load, presents a "Login with Spotify" button.
The button constructs an auth URL according to the Spotify PKCE authorization flow, and directly sets window.location.href
to that constructed URL:
//spotifyAuth.js
export const getAuthorizationURL = async () => {
logMessage(`Getting authorization URL...`);
const authUrl = new URL("https://accounts.spotify.com/authorize");
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Save the codeVerifier in IndexedDB
await setCachedEntry('auth', codeVerifier, 'spotify_code_verifier');
logMessage(`Code verifier: ${codeVerifier}`);
const state = generateState();
const params = new URLSearchParams({
response_type: 'code',
client_id: CLIENT_ID,
scope: 'user-library-read',
state: state,
redirect_uri: REDIRECT_URI,
code_challenge_method: 'S256',
code_challenge: codeChallenge,
});
authUrl.search = new URLSearchParams(params).toString();
const authorizationURL = authUrl.toString();
logMessage(`Authorization URL: ${authorizationURL}`);
return { authorizationURL };
};
export const redirectToAuthorizationUrl = async () => {
logMessage(`Redirecting to authorization URL...`);
window.location.href = (await getAuthorizationURL()).authorizationURL;
}
To start off with, We are just trying to test that clicking that button redirects to the correct URL.
We read that a single Cypress test cannot span across multiple origins. So, instead of allowing the redirect and attempting to read the new URL, we tried to stub the redirect, block it and read what the application is trying to set window.location.href
to:
//login.cy.js
describe('Login Functionality', () => {
describe('When you click Login to Spotify', () => {
it('constructs the Spotify auth url', () => {
cy.window().then(window => {
cy.stub(window.location, 'href').as('redirect')
})
cy.visit('/');
cy.get('.login-button').click();
cy.get('@redirect').should('be.calledWithMatch', `accounts.spotify.com/authorize`)
});
});
});
That resulted an an AssertionError
: Timed out retrying after 4000ms: expected redirect to have been called with arguments matching "accounts.spotify.com/authorize", but it was never called
.
We then read that you cannot stub window.location.href
directly like that. So, we tried to change the application code to set the auth URL in a function and stub that function instead:
//spotifyAuth.js
export const redirectToAuthorizationUrl = async () => {
logMessage(`Redirecting to authorization URL...`);
const authUrlToNavigateTo = (await getAuthorizationURL()).authorizationURL;
window.redirectToSpotifyAuth(authUrlToNavigateTo);
}
window.redirectToSpotifyAuth = function (authUrl) {
window.location.href = authUrl;
};
//login.cy.js
describe('Spotify Login Flow', () => {
it('should construct correct Spotify auth URL', () => {
// Stub the redirect function
cy.visit('http://localhost:3000/', {
onBeforeLoad(window) {
window.redirectToSpotifyAuth = () => {};
cy.stub(window, 'redirectToSpotifyAuth').as('redirect');
},
});
cy.get('.login-button').click();
// Check that redirectToSpotifyAuth was called with the correct URL
cy.get('@redirect').should('be.calledWithMatch', `accounts.spotify.com/authorize`);
});
});
This resulted in the same AssertionError
.
What are we doing wrong?
Share Improve this question edited Jan 23 at 0:14 Aladin Spaz 9,6981 gold badge18 silver badges39 bronze badges asked Jan 22 at 21:01 Milo CowellMilo Cowell 374 bronze badges1 Answer
Reset to default 6In your 2nd example you have set the window.redirectToSpotifyAuth
function in the same file as the handler that uses it.
The onBeforeLoad
event is probably too early to to set the stub, as it's unlikely window.redirectToSpotifyAuth
has been added at that point.
Your stub must be created after window.redirectToSpotifyAuth
is initialized, and before the call to the click handler.
Try moving the setup of window.redirectToSpotifyAuth
into a useEffect()
of the component that owns the Login button,
function MyComponent() {
useEffect(() => {
window.redirectToSpotifyAuth = function (authUrl) {
window.location.href = authUrl;
}
}, []); // on mounting
return <button onClick={window.redirectToSpotifyAuth(...)}>Login</button>;
}
that way it is set up well in advance of when the test clicks the Login button.
I mocked up a simple web app to simulate the situation.
<body>
<button onclick="redirectToSpotifyAuth('somewhere')">Login</button>
<script>
window.redirectToSpotifyAuth = function (authUrl) {
window.location.href = authUrl;
}
</script>
</body>
Then in the test I waited for the redirectToSpotifyAuth
function to exist before stubbing it (to try and avoid any race condition).
Once the stub is in place, I clicked the button to invoke the method.
it('stubs a redirect', () => {
cy.window().should('have.property', 'redirectToSpotifyAuth')
cy.window().then(win => {
cy.stub(win, 'redirectToSpotifyAuth')
.callsFake(console.log) // just for debugging
.as('redirect');
})
cy.get('button').click() // invoke the method
cy.get('@redirect').should('calledWith', 'somewhere')
})
NOTE
window.redirectToSpotifyAuth = () => {};
masks the real error in the second scenario, since it applies it's own version of the method.
If you remove it you may find the cy.stub()
fails because the method is not yet set up, and that error is more informative than a fail on cy.get('@redirect')
.
If you're still having trouble with this, post the React app (a representative cut-down example) and I'll run a test for it.
本文标签:
版权声明:本文标题:javascript - How can I test, using Cypress, that a button correctly redirects to the expected URL? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738538723a2095052.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论