angularjs-directive-renderer
v2.0.3
Published
A tool for rendering AngularJS directives, aimed for UI tests
Downloads
3
Maintainers
Readme
angularjs-isolate-directive
Render a directive in an isolated environment. Great for UI tests of components on AngularJS 1.x
Ever wished you could use Storybook on your legacy AngularJS project?
Well, you still can't. But you can use this library to achieve very close results.
Read the instructions below to see how you can write full UI tests for your components (with actual CSS!).
Features
- :door: Isolated directive - test only what you need!
- :white_check_mark: Simple and easy - try and see for yourself!
- :art: Using real CSS - screenshots are fun
- :point_right: Interactive tests - click, hover, drag...
- :ok_woman: Flexible - inject custom
$scope
as needed - :rocket: No dependencies - even AngularJS is used from your app!
- :electric_plug: Cypress integration
Install
npm install --save-dev angularjs-directive-renderer
API Reference
renderIsolatedDirective
The library exports a main function - renderIsolatedDirective(config)
,
which can be used to load a directive on an (almost) isolated environment (*).
The function expects a single config
object, with the following properties:
doc
: Document (Optional) - the document of the page which loaded your application. If omitted, the globaldocument
will be used insteadinjectedScopeProperties
: Object (Optional) - default value is an empty object ({}
). Specify the scope properties which will be passed to the scope of the created directive instance It is important to note that the tested directive will receive a new scope. If you wish to access the injected scope directly from your scope, without any other tools, then you should bind your data to a property of the scope, and not directly to the scope. Read more on this StackOverflow question, and on AngularJS wiki - JavaScript Prototypal InheritancetemplateToCompile
: String - the template which will render the tested directive instance Don't forget the injected properties, that should match the data frominjectedScopeProperties
(if it exists).
* (Almost) isolated environment
Since the library requires a document of a loaded application,
it is possible that the loaded page will perform actions that will affect the test.
From this reason, it is recommended to use a page which simply loads all of your assets (scripts and stylesheets).
How it works
When rendering a test directive from template, the actions are performed:
- A clone of the original HTML element is generated in memory.
- All of the children of the body of the cloned HTML element are removed.
- A new ng-app wrapper
<div>
element is added to the cloned HTML body. The wrapper element has adata-ng-app
attribute, with the name of theng-app
attribute from the original document, which this test runs on. - Next, the original HTML element is removed from the document, and the new (cloned) HTML element is added to the document.
- The template is inserted to the application wrapper element. The template is compiled and injected with the properties that the user specified.
Cypress commands
If you use Cypress, the library will automatically add a few useful commands to Cypress when it is import
-ed.
cy.renderIsolatedDirective
- loads an isolated directive using Cypress' IFrame's document as the tested document. The function requires the same configuration object as the library'srenderIsolatedDirective
, except for thedoc
property - Cypress will supply the document. Ifdoc
will be defined, it will be used instead of Cypress' document This way, you can easily test your app using the following flow:
beforeEach(() => {
cy.visit('http://localhost:1234'); //webpack-dev-server for example, which serves your entire app
});
it('should look as expected', () => {
cy.renderIsolatedDirective({
templateToCompile: '<my-awesome-directive/>'
})
.screenshot();
});
cy.getTestedElementScope()
- quickly access your compiled element's scope, and start a Cypress chain: For example:
cy.getTestedElementScope().then(scope => expect(scope.myBoolean).to.be.false);
cy.getAngular()
- access your application's globalangular
object. Can be used for special requirements, where you wish to useangular
directly from the running application.cy.getTestedDirectiveDomElement()
- quickly access your compiled directive's DOM element, and start a Cypress chain:
cy.renderIsolatedDirective({
templateToCompile: '<my-awesome-directive/>'
})
.find('input')
.click()
.screenshot();
cy.getTestedDirectiveDomElement()
.click()
.screenshot();
You can also access the element using cy.get('@testedDirectiveElement')
(which is exactly what cy.getTestedDirectiveElement
does).
IMPORTANT NOTE - this function is used to access the DOM element, and not the compiled AngularJS element.
This means the the element can be interacted with (using click()
for example),
but it will not have AngularJS' functionality, like scope()
.
If you only need the element's scope, use the convenient method cy.getTestedElementScope()
.
If you need the element as an AngularJS element, use in combination with cy.getAngular()
.
Usage example
The following example will load the tested directive as an isolated component:
import renderIsolatedDirective from 'renderIsolatedDirective'; //1
describe('my-directive', () => {
beforeEach(() => {
//load your application, with all your `/dist` files
});
it('should load my-directive, and displayed properly', () => {
const data = { //2
name: 'test',
testArr: ['wow', 'much', 'data'],
};
const testedElement = renderIsolatedDirective({
doc: document, //3
templateToCompile: `<my-directive name="data.name" some-array="data.testArr"/>`, //4
injectedScopeProperties: {data}, //5
});
testedElement.find('#my-button').click();//6
});
});
Explanation:
- Import the rendering function of this library.
- Create the data which will be injected to the tested directive.
doc
(Optional) - Inject the document of the page which loaded your application.
If not specified, the global variabledocument
will be used.
It is expected thatangular
will be defined on the page, and that anng-app
will exist somewhere on this page.templateToCompile
- Specify the template which will render the tested directive instance (don't forget the injected properties, that should match the data from step 2).injectedScopeProperties
(Optional) - specify the scope properties which will be passed to the scope of the created directive instance (data is copied, and then injected by$compile
service).
Default value is an empty object ({}
)- Interact with the returned element, as test it as you wish.
Development
CI
When contributing, keep in mind the following CI flow:
- The code is added to a locally-cloned repo
- A commit is added using
Commitizen
, by runningnpm run commit
- After a PR, the contributed code is pushed to the repo
Travis
build is triggered, and runs the following (each failed step prevents future steps):- Test
- Generate changelog automatically by using
Semantic Release
.
If a new version will be deployed to npm (see next step), update the changelog and add it to git. - Publish a new version to npm if needed.
Semantic Release
will scan the commits to see which changes were added since the last release.
This way,Semantic Release
will automatically decide which version (major, minor or patch upgrade) should be published. - The changelog is automatically added to
git
(again, bySemantic Release
).
A new tag which matches the npm version from previous step is added to the current commit. The commit will be authored bysemantic-release-bot
Commit Messages
This project uses Commitizen for commit messages conventions.
When committing, you should use Commitizen
to generate the commit message in AngularJS Git Commit Message Convention.
For that, you can run npm run commit
.