ember octane test-helpers: currentURL() returns addressbar URL() - ember.js

I noticed currentURL() from #ember/test-helpers returns the actual test window address bar URL instead of the test's currentUrl().
I made sure that my ENV.locationType=none. Can someone spot something really obvious that I'm missing?
Expected behavior: when user visits '/clientname', they should be redirected to '/clientname/login':
config/environment.js:
module.exports = function (environment) {
let ENV = {
modulePrefix: "portal-client3",
environment,
rootURL: "/",
locationType: "auto",
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false,
},
},
APP: {},
};
if (environment === "test") {
ENV.locationType = "none";
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = "#ember-testing";
ENV.APP.autoboot = false;
}
return ENV;
};
app/router:
import EmberRouter from "#ember/routing/router";
import config from "./config/environment";
export default class Router extends EmberRouter {
location = config.locationType;
rootURL = config.rootURL;
}
tests/test-helper:
import Application from "../app";
import config from "../config/environment";
import { setApplication } from "#ember/test-helpers";
import { start } from "ember-qunit";
setApplication(Application.create(config.APP));
start();
tests/acceptance/login-test:
import { module, test } from "qunit";
import { visit, currentURL } from "#ember/test-helpers";
import { setupApplicationTest } from "ember-qunit";
import { setupMirage } from "ember-cli-mirage/test-support";
module("Acceptance | login", function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
test("visiting /login", async function (assert) {
await visit("/clientname");
assert.equal(currentURL(), "/clientname/login");
// I see currentUrl()='/tests/login' instead of '/clientname/login'
});
});
Screenshot:

Related

can't run apollo-server-express with merged typeDefs and resolvers using #graphql-tools/merge

hey friends this is the structure of my project and files down below:
starting from app.js file:
import { Server } from "./src/Server";
import { config } from "dotenv";
config();
Server.StartServer();
Down below is the Server.ts file that is bootstrapping of the apollo-server-express
import express from "express";
import http from "http";
import { ApolloServer } from "apollo-server-express";
import { GraphQLServerOptions } from "apollo-server-core/src/graphqlOptions";
import { schema } from "./graphql/index";
export class Server {
public static StartServer() {
const app: express.Application = express();
const server = new ApolloServer({
schema,
graphiql: true,
} as unknown as GraphQLServerOptions);
server.applyMiddleware({ app });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(process.env.PORT, function () {
console.log(`server is running on port ${process.env.PORT}`);
});
}
}
this is user.resolvers.ts file that my resolver goes here:
import { IResolvers } from "graphql-tools";
export const resolver: IResolvers = {
Mutation: {
register: function (parent, args, content, info) {
console.log(parent);
},
},
Query: {
getUser: function (parent, args, content, info) {
console.log(parent);
},
},
};
And here we go with the typeDef for in user.schema.ts file:
import { gql } from "apollo-server-express";
export const typeDef = gql`
type User {
username: String
password: String
}
type Mutation {
register(input: {username: String!, passwprd: String!})
}
type Query {
getUSer(username): User
}
`;
And finally over there in ./src/graphql/index.ts file I'm doing the mergig for resolvers and typeDefs there and I'm making the executableSchema for adding it to ApolloServer config object but I face the error below:
Any Ideas and suggestions would be greatly appreciated. Early thanks for the contributors :)

How to get UserId and set it as a global variable using useContext, useState and useEffect in React-Native?

I have an app built with React-Native, Amplify, AppSync and Cognito and when it loads I would like to save the USER ID and USER TYPE as a global state that can be accessed on every screen.
The user id and user type (Teacher or Student) will never change as these are created on signup.
import React, { useEffect, useState, useReducer } from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import App from './src/AppNavigation';
import Amplify, { API, graphqlOperation, Auth } from 'aws-amplify';
import awsmobile from './aws-exports';
import { getUser } from './src/graphql/queries';
Amplify.configure(awsmobile);
export const UserContext = React.createContext()
function MyApp() {
const [userContext, setUserContext] = useState({})
const getUserIdAndType = async () => {
try {
// get User data
const currentUser = await Auth.currentAuthenticatedUser();
const userId = await currentUser.signInUserSession.accessToken.payload.sub;
// get user data from AppSync
const userData = await API.graphql(graphqlOperation(getUser, { id: userId }));
setUserContext({ userId: userId, userType: userData.data.getUser.userType })
} catch (err) {
console.log('error', err);
}
}
useEffect(() => {
getUserIdAndType()
}, [])
return (
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
);
}
AppRegistry.registerComponent(appName, () => MyApp);
Then when I want to use the context state I do as follows:
import { useContext } from 'react';
import { UserContext } from '../../../index';
function Loading ({ navigation }) {
const userContext = useContext(UserContext)
if (userContext.userId != '') {
navigation.navigate('AppTabs');
} else {
navigation.navigate('Auth');
}
}
export default Loading;
Or to get which screen to show (Teacher or Student)...
import { useContext } from 'react';
import { UserContext } from '../../../index';
function LoadingProfile ({ navigation }) {
const userContext = useContext(UserContext)
if (userContext.userType === 'Teacher') {
navigation.navigate('TeacherScreen');
} else if (userContext.userType === 'Student') {
navigation.navigate('StudentScreen');
}
}
export default LoadingProfile;
When the app loads it says the userContext.userId and userContext.userType are empty so it is not saving the state when I set it in the getUserIdAndType() function.
-
****** If I rewrite the App file (INSTEAD OF USING THE HOOKS useState, useEffect) I just declare the values then it works... so I am obviously not using the hooks or async getUserIdAndType() correctly. ******
import React, { useEffect, useState, useReducer } from 'react';
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
import App from './src/AppNavigation';
import Amplify, { API, graphqlOperation, Auth } from 'aws-amplify';
import awsmobile from './aws-exports';
import { getUser } from './src/graphql/queries';
Amplify.configure(awsmobile);
export const UserContext = React.createContext()
function MyApp() {
const userContext = {
userId: '123456789', // add the user id
userType: 'Teacher', // add the user type
}
return (
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
);
}
AppRegistry.registerComponent(appName, () => MyApp);
change this :
<UserContext.Provider value={{userContext}}>
<App />
</UserContext.Provider>
to this :
<UserContext.Provider value={userContext}>
<App />
</UserContext.Provider>
you've added an extra curly bracket " { "

this.$apollo always undefined

I'm trying to use apollo (+ vue, django) but for some reason it won't get loaded/used in a component,this.$apollo is always undefined.
<script>
import { GET_ALL_USERS_QUERY } from '../js/graphql/queries/userQueries.js'
export default {
name: 'GraphQLTest',
data() {
return {
users: [],
loading: true
}
},
async mounted() {
this.loading = true
this.users = await this.$apollo.query({
query: GET_ALL_USERS_QUERY
})
this.loading = false
}
}
</script>
[Vue warn]: Error in mounted hook (Promise/async): "TypeError: Cannot read property 'query' of undefined"
main.js
import Vue from 'vue'
import { router } from './routes.js'
import store from './store.js'
import { createProvider } from './apollo.js'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
router,
store,
provide: createProvider(),
render: h => h(App),
}).$mount('#app')
apollo.js
import Vue from 'vue'
import VueApollo from 'vue-apollo'
import { createApolloClient, restartWebsockets } from 'vue-cli-plugin-apollo/graphql-client'
// Install the vue plugin
Vue.use(VueApollo)
// Name of the localStorage item
const AUTH_TOKEN = 'jwt-token'
// Config
const defaultOptions = {
httpEndpoint: '/graphql',
wsEndpoint: null,
tokenName: AUTH_TOKEN,
persisting: false,
websocketsOnly: false,
ssr: false
}
// Call this in the Vue app file
export function createProvider (options = {}) {
// Create apollo client
const { apolloClient, wsClient } = createApolloClient({
...defaultOptions,
...options
})
apolloClient.wsClient = wsClient
// Create vue apollo provider
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
defaultOptions: {
$query: {
loadingKey: 'loading',
fetchPolicy: 'cache-and-network'
}
},
errorHandler (error) {
// eslint-disable-next-line no-console
console.log('%cError', 'background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;', error.message)
}
})
return apolloProvider
}
// Manually call this when user log in
export async function onLogin (apolloClient, token) {
localStorage.setItem(AUTH_TOKEN, token)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (login)', 'color: orange;', e.message)
}
}
// Manually call this when user log out
export async function onLogout (apolloClient) {
localStorage.removeItem(AUTH_TOKEN)
if (apolloClient.wsClient) restartWebsockets(apolloClient.wsClient)
try {
await apolloClient.resetStore()
} catch (e) {
// eslint-disable-next-line no-console
console.log('%cError on cache reset (logout)', 'color: orange;', e.message)
}
}
Why is apollo never loaded? something missing in the config?
[edit: same thing happens when following the tutorial : ]
import Vue from "vue"
import App from "./App.vue"
import { router } from './routes.js'
import ApolloClient from "apollo-boost"
import VueApollo from "vue-apollo"
const apolloProvider = new VueApollo({
defaultClient: new ApolloClient({
uri: "http://localhost:8000/graphql/"
})
})
Vue.use(VueApollo)
new Vue({
router,
el: "#app",
provide: apolloProvider.provide(),
render: h => h(App)
})
if you created your VUE project using vue-cli > 3 this will cause the issue with this.$apollo equal to undefined.

How to unit test VueJS watcher on $route

I'm testing a Single file component that uses vue router to watch $route. The problem is that I can't get the test to both change the route and trigger the watcher's function.
The test file:
import { createLocalVue, shallow } from 'vue-test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
const localVue = createLocalVue();
localVue.use(Vuex);
const $route = {
path: '/my/path',
query: { uuid: 'abc' },
}
wrapper = shallow({
localVue,
store,
mocks: {
$route,
}
});
it('should call action when route changes', () => {
// ensure jest has a clean state for this mocked func
expect(actions['myVuexAction']).not.toHaveBeenCalled();
vm.$set($route.query, 'uuid', 'def');
//vm.$router.replace(/my/path?uuid=def') // tried when installing actual router
//vm.$route.query.uuid = 'def'; // tried
//vm.$route = { query: { uuid: 'def'} }; // tried
expect(actions['myVuexAction']).toHaveBeenLastCalledWith({ key: true });
});
My watch method in the SFC:
watch: {
$route() {
this.myVuexAction({ key: true });
},
},
How do you mock router in such a way that you can watch it and test the watch method is working as you expect?
This is how I'm testing a watch on route change that adds the current route name as a css class to my app component:
import VueRouter from 'vue-router'
import { shallowMount, createLocalVue } from '#vue/test-utils'
import MyApp from './MyApp'
describe('MyApp', () => {
it('adds current route name to css classes on route change', () => {
// arrange
const localVue = createLocalVue()
localVue.use(VueRouter)
const router = new VueRouter({ routes: [{path: '/my-new-route', name: 'my-new-route'}] })
const wrapper = shallowMount(MyApp, { localVue, router })
// act
router.push({ name: 'my-new-route' })
// assert
expect(wrapper.find('.my-app').classes()).toContain('my-new-route')
})
})
Tested with vue#2.6.11 and vue-router#3.1.3.
I checked how VueRouter initializes $route and $router and replicated this in my test. The following works without using VueRouter directly:
const localVue = createLocalVue();
// Mock $route
const $routeWrapper = {
$route: null,
};
localVue.util.defineReactive($routeWrapper, '$route', {
params: {
step,
},
});
Object.defineProperty(localVue.prototype, '$route', {
get() { return $routeWrapper.$route; },
});
// Mock $router
const $routerPushStub = sinon.stub();
localVue.prototype.$router = { push: $routerPushStub };
const wrapper = shallowMount(TestComponent, {
localVue,
});
Updating $route should always be done by replacing the whole object, that is the only way it works without using a deep watcher on $route and is also the way VueRouter behaves:
$routeWrapper.$route = { params: { step: 1 } };
await vm.wrapper.$nextTick();
Source: install.js
Its working for me
let $route = {
name: 'any-route',
};
We defined a $route and we called like
wrapper = mount(YourComponent, {
mocks: {
$route,
},
});
and my componente is like this
#Watch('$route', { deep: true, immediate: true, })
async onRouteChange(val: Route) {
if (val.name === 'my-route') {
await this.getDocumentByUrl();
await this.allDocuments();
}
};
pd: I use typescript, but this work with the another format
and finally my test
it('my test', ()=>{
const getDocumentByUrl = jest.spyOn(wrapper.vm, 'getDocumentByUrl');
const allDocuments = jest.spyOn(wrapper.vm, 'allDocuments');
wrapper.vm.$route.name = 'my-route';
await flushPromises();
expect(getDocumentByUrl).toHaveBeenCalled();
expect(allDocuments).toHaveBeenCalled();
})
The way to do this actually is to use vue-test-utils wrapper method, setData.
wrapper.setData({ $route: { query: { uuid: 'def'} } });

ember-simple-auth-oauth2 and ember-cli: serverTokenRevocationEndpoint not honored

I'm using the following libraries:
ember-cli: 0.2.0.beta.1
ember-cli-simple-auth: 0.7.3
ember-cli-simple-auth-oauth2: 0.7.3
The simple-auth libs were installed like so:
ember install:addon ember-cli-simple-auth
ember install:addon ember-cli-simple-auth-oauth2
I've been trying to get simple-auth configured with the standard simple-auth Oauth2 authenticator simple-auth-authenticator:oauth2-password-grant which seems to be mandatory to put in my login controller that mixed LoginControllerMixin (not sure why we have the ENV['simple-auth'] = { authenticator: ' ... ' }; option since it's not honored?) and trying to set the following end points:
serverTokenRevocationEndpoint: '/revoke'
serverTokenEndPoint: '/test'
no matter how I put things in the config/environment.js it just doesn't get honored. My end point remains the default /token and the revocation point is not in effect.
Do I need to create a custom Oauth2 authenticator class for my settings to be used?
I thought configuring it would kick off the standard classes and just work, no?
Here's what I have so far:
controllers/login.js
import Ember from 'ember';
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export
default Ember.Controller.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:oauth2-password-grant'
});
config/environment.js
/* jshint node: true */
var Auth = require('./auth.js');
module.exports = function(environment) {
var ENV = {
modulePrefix: 'mbo',
environment: environment,
baseURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
}
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:oauth2-bearer'
// routeAfterAuthentication: 'user.dashboard'
};
ENV['simple-auth-oauth2'] = Auth.dev.internal;
}
if (environment === 'test') {
// Testem prefers this...
ENV.baseURL = '/';
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
// ENV['simple-auth'] = {
// serverTokenEndpoint: '/api/v2/test',
// serverTokenRevocationEndpoint: '/api/v2/logout'
// }
}
if (environment === 'production') {
// ENV['simple-auth'] = {
// serverTokenEndpoint: '/token',
// serverTokenRevocationEndpoint: '/logout'
// }
}
return ENV;
};
conf/auth.js
module.exports = {
dev: {
external: {
},
internal: {
serverTokenEndpoint: '/token',
serverTokenRevocationEndpoint: '/logout'
}
},
prod: {
external: {
},
internal: {
serverTokenEndpoint: '/token',
serverTokenRevocationEndpoint: '/logout'
}
}
};
As is, the authenticate method send the request to /token and the invalidateSession invalidates the session but sends no request to the back-end.