I'm writing a Vue app that uses vue-apollo to interact with graphql. I'm wondering if it's possible to stub the graphql requests. I thought this should work:
it('should access a story', function() {
cy.server();
cy.route('http://localhost:3002/graphql', {
data: {
Story: { id: 2, title: 'story title', content: 'story content' }
}
});
cy.visit('/stories/2');
});
Unfortunately, I get an error from graphql complaining that id is an Int instead of an ObjectId. Am I missing something?
The problem was that stubbing fetch requests isn't yet implemented in Cypress (which is what Vue Apollo is using). I ended up following these instructions:
Install github/fetch
Add this to cypress/support/index.js:
.
Cypress.on('window:before:load', win => {
win.fetch = null;
win.Blob = null;
});
Now it works!
I got it working with this package here:
npm i #iam4x/cypress-graphql-mock
Add this line to 'support/commands.js'
import "#iam4x/cypress-graphql-mock";
go to your graphiql playground and download your schema
add task command to 'plugins/index.js' (REMEMBER TO CHANGE PATH TO SCHEMA FILE YOU DOWNLOADED EARLIER)
module.exports = (on, config) => {
on("task", {
getSchema() {
return fs.readFileSync(
path.resolve(__dirname, "../../../schema.graphql"),
"utf8"
);
}
});
};
write your tests with loaded schema
beforeEach(() => {
cy.server();
cy.task("getSchema").then(schema => {
cy.mockGraphql({
schema
});
});
});`
describe("Login Form", () => {
it("should redirect after login", () => {
cy.mockGraphqlOps({
operations: {
Login: {
login: {
jwt: "some-token",
user: {
id: "5d5a8e1e635a8b6694dd7cb0"
}
}
}
}
});
cy.visit("/login");
cy.getTestEl("email-input").type("Max Mustermann");
cy.getTestEl("password-input").type("passwort");
cy.getTestEl("submit").click();
cy.getTestEl("toolbar-title").should("exist");
});
})
Visit the original repo for further explanation as i find it less confusing. The package you have installed is just a working fork of this one:
https://github.com/tgriesser/cypress-graphql-mock
Related
In the test/posts/posts.e2e-spec.ts file
import { INestApplication } from '#nestjs/common';
import { TypeOrmModule } from '#nestjs/typeorm';
import { Test, TestingModule } from '#nestjs/testing';
import request = require('supertest');
import { PostsModule } from '../../src/posts/posts.module';
describe('Posts (e2e)', () => {
const posts = {
id: 1,
name: 'FirstPost #1',
};
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
...
}),
PostModule,
],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
describe('post', () => {
it('should retrieve all post data', async () => {
request(app.getHttpServer())
.post('/graphql')
.send({
query:
`{findPosts() {
name
}}`,
})
.expect(200)
.expect((res) => {
console.log(res.body.data)
expect(res.body.data.post.length).toEqual(posts.length)
})
})
})
});
I created migration and inserted data into database first, then run this test, it can't go to the expect items. Even set console log I can't see anything in the output.
So maybe the /graphql can't be access in this way? I can access the endpoint from browser as http://localhost:3000/graphql.
If import supertest as
import * as request from 'supertest';
In the line request it showed:
This expression is not callable. Type ‘typeof supertest’ has no call signatures.
The version of them:
supertest: 6.1.3
#types/supertest: 2.0.11
Check out this very useful link https://github.com/jmcdo29/testing-nestjs/tree/main/apps/graphql-sample. It explains a lot of things regarding tests including graphql nestjs testing along with sample application
I’m trying to get apollo-server-lambda or apollo-server-express to work with an executable schema for v3.36.
Here are the packages we use:
apollo-server-express#3.36 or apollo-server-lambda#3+
graphql-constraint-directive#3.0.0
#graphql-tools/schema#7.1.3
I ran multiple regression test to make it work, and it does not seem to hit GraphQL.
Here’s my Apollo server config:
const apolloServer = new ApolloServer({
schema: initializeSchema(),
plugins: [
ApolloServerPluginLandingPageGraphQLPlayground(),
{
didEncounterErrors(errors) {
logger.info(`didEncounterErrors:`)
logger.info(errors)
},
async requestDidStart(requestContext) {
logger.info(`Request started! ${requestContext}`);
return {
async parsingDidStart(requestContext) {
logger.info(`Parsing started! ${requestContext}`);
},
async validationDidStart(requestContext) {
logger.info(`Validation started! ${requestContext}`);
}
}
},
}],
context: async ({ event, context, express }) => {
logger.info(`Loading event... ${JSON.stringify(event)}`)
const newContext = {
headers: event.headers,
functionName: context.functionName,
event,
context,
expressRequest: express.req,
user: {} ?? null,
}
logger.info(`context ${JSON.stringify(newContext)}`)
return newContext
},
dataSources: () => {
logger.info('!initializing datasource')
initializeDbConnection()
return {}
},
...(['staging', 'production', 'demo'].includes(process.env.stage as string)
? { introspection: false, playground: false }
: {}),
})
I was able to log the executable schema inside initializeSchema, but it does not seem to hit the GraphQL Typedef and Resolver after upgrading. It just goes straight to context. So, I'm kinda stumped how to make HTTP request hit the Typedef and Resolvers using makeExecutableSchema()
I just need some advise or a list of table that could help me which version works best with the given apollo-server-<server_version>.
I am trying to create a custom controller to update the user profile.
I created the routing file and the corresponding controller.
Routing file: server/src/api/profile/routes/profile.js
module.exports = {
routes: [
{
method: 'GET',
path: '/profile',
handler: 'profile.getProfile',
},
{
method: 'PUT',
path: '/profile',
handler: 'profile.updateProfile',
},
]
}
Controller: src/api/profile/controllers/profile.js
async updateProfile(ctx) {
try {
const { id } = ctx.state?.user;
const user = strapi.query('admin::user').update({
where: { id },
data: {
username: "testUsername"
}
})
ctx.body = "User updated"
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
The above code returns "User updated", but the username does not update. I am executing the PUT call with a correct Bearer authorisation token and the user permissions for that user are set to enable "updateProfile".
Oddly enough, the same code, when changed to update a different API item, works perfectly fine:
async updateArticle(ctx) {
try {
const { id } = ctx.state?.user;
const article = strapi.query('api::article.article').update({
where: { author: id },
data: {
title: "New title"
}
})
ctx.body = article
} catch(error) {
ctx.badRequest("Something went wrong", { error })
}
},
I am also confused by different syntaxes appearing in the official Strapi documentation, for example some docs mention:
strapi.query('admin::user').update({ id }, data)
But in other places in the documentation its:
strapi.plugins['users-permissions'].services.user.update({ id });
And then elsewhere:
strapi.query('user', 'users-permissions').update(params, values);
Another question is: do I need to sanitise the input / output in any way? If yes, how? Importing sanitizeEntity from "Strapi-utils" doesn't work, but it's mentioned in several places on the internet.
Additionally, I cannot find a list of all ctx properties. Where can I read what is the difference between ctx.body and ctx.send?
The lack of good documentation is really hindering my development. Any help with this will be greatly appreciated.
I am trying to get nuxt-socket-io working on my NuxtJS application, but my emit functions do not seem to trigger the actions in my vuex store.
nuxt.config.js has the following code:
modules: [
'#nuxtjs/axios',
'nuxt-socket-io'
],
io: {
sockets: [
{
name: 'main',
url: process.env.WS_URL || 'http://localhost:3000',
default: true,
vuex: {
mutations: [],
actions: [ "subscribeToMirror" ],
emitBacks: []
},
},
]
},
That subscribeToMirror action is present in my vuex store (in index.js):
export const actions = {
async subscribeToMirror() {
console.log('emit worked');
try {
new TopicMessageQuery()
.setTopicId(state.topicId)
.setStartTime(0) // TODO: last 3 days
.subscribe(HederaClient, res => {
console.log("Got response from mirror...");
return res;
});
} catch (error) {
console.error(error);
}
}
};
And that action should be triggered by the emit in my index.vue script:
mounted() {
this.socket = this.$nuxtSocket({
name: 'main',
reconnection: false
})
},
methods: {
...mapMutations([
'setEnv',
'initHashgraphClient',
'setTopicId',
'createNewTopicId'
]),
...mapActions([
'createAndSetTopicId'
]),
subscribeToMirror() {
console.log("method worked");
return new Promise((res) => {
this.socket.emit('subscribeToMirror', {}, (resp) => {
console.log(resp)
resolve()
})
})
}
}
While I can see the 'method worked' console output from index.vue's subscribeToMirror method, I have never seen the 'emit worked' message. I have played around with this for hours, copying the instructions from this guide but have had no success.
Can anyone spot what I'm doing wrong?
UPDATE: I tried copying the code from this example and was unable to get a response from that heroku page. So it appears that I am completely unable to emit (even though $nuxtSocket appears to be functional and says it's connected). I am able to confirm that the socket itself is up and running, as I was able to get responses from it using the ticks from that example. I'm putting the repo for this project up here for viewing.
UPDATE2: I made a much simpler project here which is also not functioning correctly but should be easier to examine.
It turns out that while nuxt-socket-io functions as a wrapper for socket.io stuff you still need to actually create the server. This is a good template for how to do this
When running e2e tests with Cypress, my goal is to mock a specific graphql query.
Currently, I can mock all requests like this:
cy.server();
cy.route('POST', '/graphql', {
data: {
foo: 'bar'
},
});
The problem is that this mocks all /graphql queries. It would be awesome if I somehow could say:
cy.route('POST', '/graphql', 'fooQuery', {
data: {
foo: 'bar'
},
});
In our application, we are using Apollo Graphql - and thus all queries are named.
With cypress 6.0 route and route2 are deprecated, suggesting the use of intercept. As written in the docs (https://docs.cypress.io/api/commands/intercept.html#Aliasing-individual-GraphQL-requests) you can mock the GraphQL requests in this way:
cy.intercept('POST', '/api', (req) => {
if (req.body.operationName === 'operationName') {
req.reply({ fixture: 'mockData.json'});
}
}
One way to go about it is to provide the mocked data for the graphql operations in question inside one fixture file
cypress/support/commands.js
Cypress.Commands.add('stubGraphQL', (graphQlFixture) => {
cy.fixture(graphQlFixture).then((mockedData) => {
cy.on('window:before:load', (win) => {
function fetch(path, { body }) {
const { operationName } = JSON.parse(body)
return responseStub(mockedData[operationName])
}
cy.stub(win, 'fetch', fetch).withArgs("/graphql").as('graphql');
});
})
})
const responseStub = result => Promise.resolve({
json: () => Promise.resolve(result),
text: () => Promise.resolve(JSON.stringify(result)),
ok: true,
})
//TODO how to get it to stop listening and trying to stub once the list of operations provided in fixture have been stubbed?
example fixture file cypress/fixtures/signInOperation.json (note that there are 2 operations in there and that's how you can specify which response to mock)
{
"SIGNIN_MUTATION": {
"data":{"signin":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"Sam","__typename":"User"}}
},
"CURRENT_USER_QUERY" : {
"data":{"me":{"id":"ck896k87jac8w09343gs9bl5h","email":"sams#automation.com","name":"!!Sam's Mock","permissions":["USER"],"cart":[{"id":"ck89gebgvse9w0981bhh4a147","quantity":5,"item":{"id":"ck896py6sacox0934lqc8c4bx","price":62022,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585253000/sickfitz/ecgqu4i1wgcj41pdlbty.jpg","title":"MensShoes","description":"Men's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gec6mb3ei0934lmyxne52","quantity":5,"item":{"id":"ck896os7oacl90934xczopgfa","price":70052,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585252932/sickfitz/i7ac6fqhsebxpmnyd2ui.jpg","title":"WomensShoes2","description":"Women's Shoes","__typename":"Item"},"__typename":"CartItem"},{"id":"ck89gl45psely0981b2bvk6q5","quantity":7,"item":{"id":"ck89ghqkpb3ng0934l67rzjxk","price":100000,"image":"https://res.cloudinary.com/deadrobot/image/upload/v1585269417/sickfitz/eecjz883y7ucshlwvsbw.jpg","title":"watch","description":"Fancy Watch","__typename":"Item"},"__typename":"CartItem"}],"__typename":"User"}}
}
}
in your spec file
cy.stubGraphQL('signInOperation.json')
cy.visit(yourURL)
cy.get(loginButton).click()
With cypress 5.1, using the new route2 command it is very simple to mock GraphQL requests, for example:
cy.route2('/graphql', (req) => {
if(req.body.includes('operationName')){
req.reply({ fixture: 'mockData.json'});
}
});
I just added an if condition to evaluate if the body of the GraphQL request contains certain string as part of the query.
If that is true, then I reply back with a custom body loaded from a fixture.
Documentation for cy.route2():
https://docs.cypress.io/api/commands/route2.html
You can try this if want to use fixture for graphql response:
cy.intercept('POST', '/test_api/graphql', (req) => {
req.continue((res) => {
if (req.body.operationName === 'op_name') {
res.send({ fixture: 'MyFixture/xyz.json' }),
req.alias = 'graphql'
}
})
})