Passing request headers to all requests using cypress - request

I want to intercept request headers for my application globally for all the requests and routes including html,js ,css
Tried below code but didn't work
tried cy.server
cy.route
code that i tried for intercepting.
'use strict'
describe(`TodoMVC using Cypress`, function () {
it('passing request header', () => {
cy.server({
whitelist: (xhr) => {
return xhr.method === 'GET' && /\.(jsx?|html|css)(\?.*)?$/.test(xhr.url)
},
onAnyRequest: (route, proxy) => {
proxy.xhr.setRequestHeader('Referer', '***'),
proxy.xhr.setRequestHeader('Authorization','***')
}
})
var options = {
url: 'http://localhost:8080',
headers: {'Referer': '***','Authorization' :'**='}
};
cy.visit(options);
})
})

Related

Why does not cypress alias work on Github CI?

I have a problem in github ci, it cannot find the alias, and think it even doesn't define that, but all is well on local. I tested on both cypress:open and cypress:run
this is the command I defiend:
Cypress.Commands.add("byPassLogin", () => {
const url = Cypress.env("api_url");
const token = "...";
cy.saveToLocalStorage("auth_token", token);
cy.intercept("POST", url, (req) => {
if (req.body.operationName === "me") {
req.reply({
statusCode: 200,
body: {
data: {
me: { id: "1", email: "test#email.com" },
},
},
});
}
}).as("byPassLogin");
});
and then I used it on beforeEach like this
describe("test account functionality", () => {
const URL = Cypress.env("api_url");
beforeEach(() => {
cy.visit("/");
cy.byPassLogin();
});
it.only("should logout when click on nav bar", () => {
cy.intercept("POST", URL, (req) => {
if (req.body.operationName === "signOut") {
req.reply({
statusCode: 200,
body: {
data: { updateUser: { errors: null, user: { id: "1" } } },
},
});
}
}).as("signOut");
cy.wait("#byPassLogin").then(() => {
cy.url().should("include", "/app");
cy.get("#account").click();
cy.get("#logout").click();
cy.wait("#signOut").then(() => {
cy.url().should("include", "/login");
});
});
});
});
I used another approach, it works on local but still not work on CI
Cypress.Commands.add("byPassLogin", () => {
const url = Cypress.env("api_url");
const token = "...";
cy.intercept("POST", url, (req) => {
req.reply({
statusCode: 200,
body: {
data: {
login: { user: { id: "1", email: "test#email.com" }, token },
},
},
});
}).as("byPassLogin");
cy.visit("/").then(() => {
cy.get("#email").type("test#email.com");
cy.get("#password").type("123456");
cy.get("button[type=submit]").click();
cy.wait("#byPassLogin").then(() => {
cy.url().should("include", "/app");
});
});
and used it like this
describe("test account functionality", () => {
const URL = Cypress.env("api_url");
beforeEach(() => {
cy.byPassLogin();
});
it.only("should logout when click on nav bar", () => {
cy.intercept("POST", URL, (req) => {
if (req.body.operationName === "signOut") {
req.reply({
statusCode: 200,
body: {
data: { updateUser: { errors: null, user: { id: "1" } } },
},
});
}
}).as("signOut");
cy.get("#account").click();
cy.get("#logout").click();
cy.wait("#signOut").then(() => {
cy.url().should("include", "/login");
});
});
error:
CypressError: Timed out retrying after 5000ms: cy.wait() timed out waiting 5000ms for the 1st request to the route: byPassLogin. No request ever occurred.
any help would be appreciated
cypress version: 8.4.1
react: 18
It seems that you should reverse the order of commands in the beforeEach()
beforeEach(() => {
cy.byPassLogin();
cy.visit("/");
})
There is nothing in the command flow between cy.intercept(...).as("byPassLogin") and cy.wait("#byPassLogin") except the signOut intercept.
As you probably know cy.intercept() is a command to set up a listener on the network requests, but it does not trigger any event in the app.
The sequence for intercepting is
set up the intercept listener
trigger the request (e.g cy.visit() or .click())
wait for the alias
Thanks to #fody I succeed to manage the issue, to find the issue I used a workflow similar to this to record my testing in dashboard.cypress.io, then I found it does not post form data to the correct endpoint URL, actually since the URL has been defined in env I needed to define it in CI as well.
That's it.
It was working since I had the env in local.

Mock Axios instance and interceptors in React component jest test

I'm testing a component that calls an API to populate a table with data. Though axios is used, axios is being wrapped in a convenience method of sorts to populate headers before executing the request via interceptors. I've tried axios-mock-adapter, but it's not working. I'm still new to testing React and I'm lost on how to mock data coming back from the api/axios. How do I go about mocking the api call to mock the data for my tests to pass??
This is my simple test:
test('<EmailTable/> ', async () => {
const { debug, getByText } = render(<CommunicationEmail />);
await waitFor(() => expect(getByText('Test Email Subject')).toBeTruthy());
}
This is the axios wrapper (api.js):
const instance = axios.create({
baseURL: `${apiUrl}/v1`,
timeout: 12000,
withCredentials: true,
headers: headers,
});
//intercept requests to validate hashed auth token
instance.interceptors.request.use((request) => {
const token = request.headers['X-Our-Access-Token'];
if (
localStorage.getItem('user_token') == null ||
SHA256(token).toString(enc.Hex) == localStorage.getItem('user_token')
) {
return request;
} else {
console.log({ what: 'Auth key invalid' });
return Promise.reject('Invalid token!');
}
});
//intercept responses to handle 401 errors
instance.interceptors.response.use(
(response) => {
return response;
},
(error) => {
// handle 401 authentication errors and redirect to SSO
if (error.response != null && error.response.status != null && error.response.status === 401) {
console.error({ what: 'Authorization error', e: error });
}
return Promise.reject(error);
}
);
export default instance;
And here's a simplification of the component I'm trying to test:
import api from './api.js';
const EmailTable = () => {
const [emails, setEmails] = useState();
useEffect(() => {
if(!emails) {
getEmails();
}
}, [emails]);
const getEmails = async () => {
await api({
method: 'GET',
url: `/communications/emails`,
}).then((response) => {
if (response.success) {
setEmails(response.emails);
}
}
}
if(!emails) { return <div> Loading... </div> };
return <div>{emails}</div>;
}
UPDATE WITH SOLUTION:
To mock the axios wrapper that is my API, I had to mock the api module and return a resolved promise like so:
jest.mock('../api', () => {
return function (request) {
// If we want to mock out responses to multiple API requests, we could do if (request.url = "/blah/blah") { return new Promise.... }
return new Promise((resolve) => {
resolve({
data: { success: true, emails: [] },
});
});
};
});

Cypress not matching routes in reactjs

I've successfully mocked several routes in testing this application. My recent work involved making a GET request on the app's startup.
export const requestSettings = () =>
dispatch => {
console.log('Requesting settings');
const url = urls.SETTINGS_HTTP;
axios.get(url)
.then(res => {
console.log('response from axios');
return dispatch(updateSettingsFromBackend(res.data));
})
.catch((e) => {
console.log('axios error', e, e.config);
return dispatch(reportError('Request for initial settings failed.'));
});
};
In Cypress I mocked this route which allowed all my tests to run (since otherwise they'd fail on the failed HTTP request at the start of each test):
// commands.js
const urls = buildUrls(Cypress.env()); // we don't have the window yet
const settingsHttpUrl = urls.SETTINGS_HTTP;
const initialSettingsForTest = { ...defaultSettingsState, displayProgressIndicator: true };
const initialPayloadForTests = _createSettingsApiPayload(initialSettingsForTest);
Cypress.Commands.add("mockGetSettings", (code: number = 200) =>
cy.route({
method: 'GET',
url: settingsHttpUrl,
status: code,
response: code === 200 ? initialPayloadForTests : {},
}));
Cypress.Commands.add("mockPutSettings", (code: number) =>
cy.route({
method: 'PUT',
url: settingsHttpUrl,
status: code,
response: {},
}));
// the tests
describe('updating settings (requestParameterChange)', () => {
beforeEach(() => {
cy.mockGetSettings(200);
cy.visitSettings();
});
...
This worked fine.
My latest branch involves waiting to make this initial request until a certain websocket message is received. In my cypress tests I am able to dispatch that websocket handler to get the sequence going and ultimately call the same GET endpoint. With my back-end running, I am able to do this in the live app. The implementation is there.
However, in this branch, my mock is just not working! With the back-end up, the functionality works (which means it's hitting the real back-end, not the mock), and with the back-end down, it fails (because the mock isn't being hit).
describe('Settings Page', () => {
beforeEach(() => {
cy.server();
cy.clock();
});
describe('initial state', () => {
beforeEach(() => {
cy.mockGetSettings(200).as('getRequest');
cy.visitSettings();
});
it('starts in a waiting state, with no settings.', () => {
cy.contains('Waiting for settings...');
});
it.only('requests the settings when it receives a FIRMM status message', () => {
const message = { data: JSON.stringify({ status: 10 }) } as MessageEvent;
cy.dispatch(handleStatusMessage(message));
cy.wait('#getRequest');
});
(Note, by the way, the mockPutSettings command earlier in this post, which DOES WORK.)
cy.wait('#getRequest') fails with
CypressError: Timed out retrying: cy.wait() timed out waiting 5000ms for the 1st request to the route: 'getRequest'. No request ever occurred.
Chrome console shows: GET http://localhost:5000/settings/ net::ERR_EMPTY_RESPONSE
In fact, taking the app itself out of the equation also fails:
it('requests the settings when it receives a FIRMM status message', () => {
cy.visit(url);
cy.wait('#getRequest');
});
In the cypress window, we can see that the route is being created/set up, but it doesn't get hit.
What's happening?
PS. axios-mock-adapter DOES work for this:
function mockGet() {
// ... setup of constants as before ...
return new MockAdapter(axios).onGet(settingsHttpUrl).reply(() => {
console.log('MOCK AXIOS HIT');
return [200, initialPayloadForTests];
});
}
describe('Settings Page', () => {
beforeEach(() => {
cy.server();
cy.clock();
mock = mockGet();
});
afterEach(() => {
mock.restore();
});
The tests work, the response comes, MOCK AXIOS HIT prints to the console. However, mock.history.get is empty.

http-proxy-middleware does't catch request from react app to API

Into my component
axios.post('/api/' + 'create', {
name: 'new name'
},
{
headers:
{
'Content-Type': 'application/json'
}
}
)
into setupProxy.js , created from third part official instruction https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development
const proxy = require('http-proxy-middleware');
module.exports = function (app) {
app.use(proxy('/api/', {
target: 'http://my-api.com/',
changeOrigin: true
}));
};
When i call method with axios from my app
into browser console write
POST http://localhost:3000/api/create 404 (Not Found)
I tryed to write /api/* and /api/** into configuration http-proxy-middleware , but it did't help me.
What it does't work?
Please try using below code, with http-proxy-middleware version 1.0.0 onwards, you cannot use proxy as the name.
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = (app) => {
app.use(createProxyMiddleware('/api',
{ target: 'http://localhost:3001/'
}));
}
Note: found this from one of the PR discussions here: https://github.com/facebook/create-react-app/issues/8550
I know its late and I came across the same issue. Keeping what worked for me so that others can give it a try.
I proxied this endpoint - https://services.odata.org/V2/Northwind/Northwind.svc/Customers?$format=json
setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = (app) => {
app.use(createProxyMiddleware('/api2', {
target: 'https://services.odata.org/V2/Northwind/Northwind.svc/',
changeOrigin: true,
pathRewrite: { '^/api2': '' }
})
);
}
Your .js file
triggerCORSCall() {
axios.get(`/api2/Customers?$format=json`)
.then(response => {
alert('Success');
}).catch(error => {
alert('Failure');
console.log(error);
})
}

TypeError: http is not a function in Jest framework

I am using React.js as my JavaScript framework, where I have installed Jest and using pact (CDC) for unit test cases and while I run the command npm run test the spec.js file is failing and throwing an error like this
TypeError: http is not a function
This is criteria-managementcomponent.spec.js file
const path = require('path')
const pact = require('pact')
const API_PORT = process.env.API_PORT || 9123
const {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
} = require('../client')
// Configure and import consumer API
// Note that we update the API endpoint to point at the Mock Service
const LOG_LEVEL = process.env.LOG_LEVEL || 'WARN'
const provider = pact({
consumer: 'Web Login',
provider: 'Web API',
port: API_PORT,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
dir: path.resolve(process.cwd(), 'pacts'),
logLevel: LOG_LEVEL,
spec: 2
})
describe('Started Testing Pact', () => {
beforeEach((done) => {
return provider.setup().then(() => done());
});
afterEach((done) => {
return provider.finalize().then(() => done())
})
describe('criteriaManagement', () => {
beforeEach(() => {
let criteriaManagement = {
uponReceiving: 'wrong criteriaManagement',
state: 'Code OK',
withRequest: {
method: 'GET',
path: '/api/criteria',
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
body: {
code: "string",
context: {},
message: "string",
payload: [
{
country: "string",
createdBy: "string",
displayOrder: 0,
id: "string",
lastModifiedBy: "string",
name: "string",
translations: {},
warehouse: "string"
}
]
}
}
};
return provider.addInteraction(criteriaManagement)
})
afterEach(() => {
return provider.verify()
})
it('Login test', done => {
criteriaManagementComponent().then((res) => {
expect(res.code).not.toBeNull();
expect(res.context).toBeDefined();
expect(res.message).not.toBeNull();
expect(res.payload.country).not.toBeNull();
expect(res.payload.createdBy).not.toBeNull();
expect(res.payload.displayOrder).toBe(0);
expect(res.payload.id).not.toBeNull();
expect(res.payload.lastModifiedBy).not.toBeNull();
expect(res.payload.translations).toBeDefined();
expect(res.payload.name).not.toBeNull();
expect(res.payload.warehouse).not.toBeNull();
});
done();
})
});
})
This is client.js file for criteria-managementcomponent.spec.js file
const request = require('superagent')
const API_HOST = process.env.API_HOST || 'http://localhost'
const API_PORT = process.env.API_PORT || 9123
const API_ENDPOINT = `${API_HOST}:${API_PORT}`
// Fetch provider data
const fetchProviderData = (submissionDate) => {
return request
.get(`${API_ENDPOINT}/provider`)
.query({ validDate: submissionDate })
.then((res) => {
console.log("Response :: ", res.body);
return {
count: 100 / res.body.count,
date: res.body.date
}
})
}
const getCriteriaManagementComponent = (accessToken, expiresIn) => {
return request
.post(`${API_ENDPOINT}/api/criteria`)
.then((res) => {
return {
code : res.body.code
}
})
}
const criteriaManagementComponent = () => {
// const request = require('request')
console.log("End Point =========> ", `${API_ENDPOINT}/api/criteria`)
return request.get(`${API_ENDPOINT}/api/criteria`).then((res) => {
return {
code : res.body.code,
context:res.body.context,
message:res.body.message,
payload:res.body.payload,
profile:res.body.payload.profile,
successful : res.body.successful
}
})
};
module.exports = {
fetchProviderData, getCriteriaManagementComponent, criteriaManagementComponent
}
It's hard to know where things are going wrong without a line number in the stack trace, however one thing is for certain - you're using a very old version of pact.
You should be using #pact-foundation/pact now.
As for the http issue, is it possible you have any mocks setup that are interfering? The issue seems unrelated to pact.
Lastly, if you could provide a reproducible github repo that others could download and run, you could probably get even better help.

Resources