REST app in Angularを作成していて、その単体テストを作成したい(もちろん!)。のリストを取得するコントローラーがあります。 jsonのRESTサービスからのブログ投稿で、要約を$ scopeに配置して、ビューに表示できるようにします。
最初は、ブログの投稿は、解析されたHTMLとしてレンダリングされるのではなく、テキスト、つまり<p>Blog body</p>
として表示されていましたが、 ng-bind-html を組み合わせて使用できることがわかりました。 $ sceサービス を使用します。これは、ブログ投稿を正しく表示するという点で正常に機能するようになりました。
この問題は、単体テスト時に発生します。いくつかのHTMLを使用してjson応答をモックし、コントローラーがHTMLを正しく処理していることをテストしようとしています。これが私のコードです:
.controller( 'HomeCtrl', function HomeController( $scope, $http, $sce ) {
$scope.posts = {};
$http.get('../drupal/node.json').success(function (data) {
var posts;
posts = data.list;
for(var i = 0; i < posts.length; i ++) {
posts[i].previewText = $sce.trustAsHtml(posts[i].body.summary);
posts[i].created = posts[i].created + '000'; // add milliseconds so it can be properly formatted
}
$scope.posts = posts;
});
})
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(inject(function ($injector) {
// Set up the mock http service responses
$httpBackend = $injector.get('$httpBackend');
// Get hold of a scope (i.e. the root scope)
$rootScope = $injector.get('$rootScope');
// The $controller service is used to create instances of controllers
var $controller = $injector.get('$controller');
$sce = $injector.get('$sce');
createController = function() {
return $controller('HomeCtrl', {
'$scope': $rootScope
});
};
}));
it('should get a list of blog posts', function() {
var rawResponse = {
"list": [
{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860"
}
]};
var processedResponse = [{
"body": {
"value": "\u003Cp\u003EPost body.\u003C\/p\u003E\n",
"summary": "\u003Cp\u003ESummary.\u003C\/p\u003E\n"
},
"created": "1388415860000",
previewText: $sce.trustAsHtml("\u003Cp\u003ESummary.\u003C\/p\u003E\n")
}];
$httpBackend.when('GET', '../drupal/node.json').respond(rawResponse);
$httpBackend.expectGET("../drupal/node.json").respond(rawResponse);
var homeCtrl = createController();
expect(homeCtrl).toBeTruthy();
$httpBackend.flush();
expect($rootScope.posts).toEqual(processedResponse);
});
});
上記をKarmaテストランナーで実行すると、次の応答が返されます。
Chrome 31.0.1650 (Windows) home section HomeCtrl should get a list of blog posts FAILED
Expected [ { body : { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ] to equal [ { body
: { value : '<p>Post body.</p>
', summary : '<p>Summary.</p>
' }, created : '1388415860000', previewText : { $$unwrapTrustedValue : Function } } ].
この問題は、$sce.trustAsHtml
が文字列ではなく関数を含むオブジェクトを返すという事実が原因であると思われます。
私の質問は、まず、私はこの問題に正しい方法で取り組んでいますか?
次に、もしそうなら、$sce.trustAsHtml
の出力をテストするにはどうすればよいですか?
Michael-bromleyの答えは私にはうまくいかなかったので、別の解決策を指摘したいと思います。私の場合、「highlight」のクラスを持つスパンを持つ別の文字列で文字列の各出現をラップするフィルターを使用していました。言い換えれば、私は言葉を強調したいのです。コードは次のとおりです。
angular.module('myModule').filter('highlight', function ($sce) {
return function (input, str) {
return $sce.trustAsHtml((input || '').replace(new RegExp(str, 'gi'), '<span class=\"highlighted\">$&</span>'));
};
});
$ sceサービスを使用して、結果の値をHTMLとして信頼します。これをテストするには、結果の値に対して$$ unwrapTrustedValue関数を使用して、テストを機能させる必要があります。
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect(result.$$unwrapTrustedValue()).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
更新:
@gugolが親切に指摘したように、$$ unwrapTrustedValueのようなAngular内部メソッドを使用しないことをお勧めします。より良いアプローチは、$ sceサービスでパブリックgetTrustedHtmlメソッドを使用することです。
it('01: should add a span with class \'highlight\' around each mathing string.', inject(function ($sce, $filter) {
// Execute
var result = $filter('highlight')('this str contains a str that will be a highlighted str.', 'str');
// Test
expect($sce.getTrustedHtml(result)).toEqual('this <span class="highlighted">str</span> contains a <span class="highlighted">str</span> that will be a highlighted <span class="highlighted">str</span>.');
}));
各テストの前に、プロバイダーを使用して$ sceを無効にする必要があります。
$ sceが無効になっている場合、すべての$ sce.trust *メソッドは、ラッパー関数ではなく元の値を返すだけです。
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
it('shall pass', inject(function($sce){
expect($sce.trustAsHtml('<span>text</span>')).toBe('<span>text</span>');
}));
あなたの特定の例では、これを行うだけです:
describe('HomeCtrl', function() {
var $httpBackend, $rootScope, $sce, createController;
beforeEach(module(function ($sceProvider) {
$sceProvider.enabled(false);
}));
// rest of the file
});
$sce.getTrusted
を使用して、最初に$sce.trustAsHtml
に渡された値、この場合はHTMLを含む文字列を返すことができることを発見しました。これにより、通常の方法で同等性をテストできます。仕方。
したがって、私のテストは次のようになります。
it('should create a previewText property using $sce.trustAsHtml', function() {
// confirms that it is an object, as should be the case when
// it has been through $sce.trustAsHtml
expect(typeof result.previewText === 'object').toEqual(true);
expect($sce.getTrusted($sce.HTML, result.previewText))
.toEqual('<p>Original HTML content string</p>');
});
もう1つのオプションは、getTrustedHtml()関数を使用して、$$ unwrapTrustedValueからhtml文字列値を取得することです。
vm.user.bio = $sce.getTrustedHtml(vm.user.bio);