J'ai essayé de rendre certains modèles liés à un état et à un composant, comme suit article
Dans mon projet fonctionnant sous dev-server, tout fonctionne bien et lorsque j'exécute $state.go("home")
le modèle de composant est chargé comme je l'attends, mais lorsque je fais cela dans un environnement de test, cela ne fonctionne pas.
Auparavant, lors des tests, lorsque j'utilisais "l'ancienne méthode" en utilisant "template" au lieu de "component" avec ui-router, j'exécutais $rootScope.$digest()
était suffisant pour ajouter le modèle à l'intérieur du <div ui-view></div>
mais en utilisant cette nouvelle méthode, cela ne fonctionne plus.
Qu'est-ce que je fais de mal ?
Editar : J'ai essayé de comprendre profondément le problème et je vois que le problème est lié à la requête HTTP qui a été faite. Peut-être est-ce lié à la façon dont ma promesse résout le callback de résolution en utilisant async/await. Veuillez vérifier le service :
Service
export class TodoService {
constructor($http, BASE_URL) {
this.http = $http;
this.url = `${BASE_URL}/todos`
}
async getTodos() {
const apiResponse = await this.http.get(this.url)
return apiResponse.data.todos
}
}
Routeur
import '@uirouter/angularjs'
export function routes($stateProvider, $locationProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false,
rewriteLinks: true,
})
$stateProvider
.state("home", {
url: "/",
component: "todoList",
resolve: {
todosList: TodoService => TodoService.getTodos()
}
})
}
Test
import { routes } from "routes"
import { TodoListComponent } from "components/todoList.component"
import { TodoService } from "services/todo.service"
describe("TodoListComponent rendering and interaction on '/' base path", () => {
let componentDOMelement
let stateService
beforeAll(() => {
angular
.module("Test", [
"ui.router"
])
.config(routes)
.constant("BASE_URL", "http://localhost:5000/api")
.component("todoList", TodoListComponent)
.service("TodoService", TodoService)
//I enable this for better logs about the problem
.run(['$rootScope','$trace', function($rootScope, $trace) {
$trace.enable("TRANSITION")
}])
})
beforeEach(angular.mock.module("Test"))
beforeEach(inject(($rootScope, $compile, $state, $httpBackend) => {
//build the scene
//1st render the root element of scene: We needs a router view for load the base path
let scope = $rootScope.$new()
componentDOMelement = angular.element("<div ui-view></div>")
$compile(componentDOMelement)(scope)
scope.$digest()
document.body.appendChild(componentDOMelement[0]) //This is a hack for jsdom before the $rootScope.$digest() call
//2nd let's create a fake server for intercept the http requests and fake the responses
const todosResponse = require(`${__dirname}/../../stubs/todos_get.json`)
$httpBackend
.whenGET(/.+\/todos/)
.respond((method, url, data, headers, params) => {
return [200, todosResponse]
})
//3rd Let's generate the basic scenario: Go at home state ("/" path)
$state.go("home")
$rootScope.$digest()
$httpBackend.flush()
}))
it("Should be render a list", () => {
console.log("HTML rendered")
console.log(document.querySelectorAll("html")[0].outerHTML)
})
})
Le résultat HTML qui ne rend pas
<html>
<head>
<style type="text/css">
@charset "UTF-8";[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate) {
display:none !important;
}
ng\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{
position:absolute;
}
</style>
</head>
<body><!-- uiView: -->
</body>
</html>
De plus, j'ai tracé le stateChange avant le HTML :
console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1276
Transition #0-0: Started -> "Transition#0( ''{} -> 'home'{} )"
console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1282
Transition #1-0: Ignored <> "Transition#1( ''{} -> 'home'{} )"
console.log node_modules/@uirouter/core/_bundles/ui-router-core.js:1313
Transition #1-0: <- Rejected "Transition#1( ''{} -> 'home'{} )", reason: Transition Rejection($id: 0 type: 5, message: The transition was ignored, detail: "undefined")
Je vois un problème dans une transition mais aucune raison n'a été donnée.
\========================================================================
Edit 2 Nous avons enfin trouvé le problème, mais je n'arrive pas à comprendre le vrai problème. . J'ai créé une branche dans mon projet pour montrer le problème. Ceci est lié à async/await
fonction javascript :
export class TodoService {
constructor($http, BASE_URL) {
this.http = $http;
this.url = `${BASE_URL}/todos`
}
//Interchange the comment on the getTodos method and run `npm run tdd` for see the problem:
//When async/await doesn't used, the html associated to the resolve in the
// "/" route that used this service, the promise was resolved that expected.
//The idea for this branch it's research about the problem and propose a way
//for we can use async/await on the production code and on the testing environment
async getTodos() {
const apiResponse = await this.http.get(this.url)
return apiResponse.data.todos
}
// getTodos() {
// return this.http.get(this.url).then(res => res.data.todos)
// }
}
Mes nouvelles questions sont donc :
- Pourquoi la façon dont j'utilise la fonctionnalité async/await n'est pas compatible avec la résolution de l'ui-router dans l'environnement de test mais fonctionne dans le code de production ?
- Peut-être est-ce lié à l'appel de $httpBackend.flush() ?
Edit 3 La question 3522 signalé dans le dépôt de routeur angular UI