├── .editorconfig
├── .gitignore
├── .jshintrc
├── .travis.yml
├── CONTRIBUTING.md
├── Gruntfile.js
├── README.md
├── bower.json
├── dist
├── angular-bootstrap-affix.js
└── angular-bootstrap-affix.min.js
├── karma.conf.js
├── package.json
├── src
└── bootstrap-affix.js
└── test
├── .jshintrc
└── spec
└── bootstrap.affix.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bower_components/
2 | /node_modules/
3 |
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": false,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": false,
18 | "strict": true,
19 | "globalstrict": true,
20 | "trailing": true,
21 | "smarttabs": true,
22 | "predef": [
23 | "angular"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.8'
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Important notes
4 | Please don't edit files in the `dist` subdirectory as they are generated via Grunt. You'll find source code in the `src` subdirectory!
5 |
6 | ### Code style
7 | Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.**
8 |
9 | ### PhantomJS
10 | While Grunt can run the included unit tests via [PhantomJS](http://phantomjs.org/), this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers.
11 |
12 | ## Modifying the code
13 | First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed.
14 |
15 | Test that Grunt's CLI and Bower are installed by running `grunt --version` and `bower --version`. If the commands aren't found, run `npm install -g grunt-cli bower`. For more information about installing the tools, see the [getting started with Grunt guide](http://gruntjs.com/getting-started) or [bower.io](http://bower.io/) respectively.
16 |
17 | 1. Fork and clone the repo.
18 | 1. Run `npm install` to install all build dependencies (including Grunt).
19 | 1. Run `bower install` to install the front-end dependencies.
20 | 1. Run `grunt` to grunt this project.
21 |
22 | Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken.
23 |
24 | ## Submitting pull requests
25 |
26 | 1. Create a new branch, please don't work in your `master` branch directly.
27 | 1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail.
28 | 1. Fix stuff.
29 | 1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done.
30 | 1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere.
31 | 1. Update the documentation to reflect any changes.
32 | 1. Push to your fork and submit a pull request.
33 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2013-07-11 using generator-angular-component v0.2.1
2 | 'use strict';
3 |
4 | module.exports = function(grunt) {
5 |
6 | // Configurable paths
7 | var yoConfig = {
8 | livereload: 35729,
9 | src: 'src',
10 | dist: 'dist'
11 | };
12 |
13 | // Livereload setup
14 | var lrSnippet = require('connect-livereload')({port: yoConfig.livereload});
15 | var mountFolder = function (connect, dir) {
16 | return connect.static(require('path').resolve(dir));
17 | };
18 |
19 | // Load all grunt tasks
20 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
21 |
22 | // Project configuration
23 | grunt.initConfig({
24 | pkg: grunt.file.readJSON('package.json'),
25 | yo: yoConfig,
26 | meta: {
27 | banner: '/**\n' +
28 | ' * <%= pkg.name %>\n' +
29 | ' * @version v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' +
30 | ' * @link <%= pkg.homepage %>\n' +
31 | ' * @author <%= pkg.author.name %> <<%= pkg.author.email %>>\n' +
32 | ' * @license MIT License, http://www.opensource.org/licenses/MIT\n' +
33 | ' */\n'
34 | },
35 | open: {
36 | server: {
37 | path: 'http://localhost:<%= connect.options.port %>'
38 | }
39 | },
40 | clean: {
41 | dist: {
42 | files: [{
43 | dot: true,
44 | src: [
45 | '.tmp',
46 | '<%= yo.dist %>/*',
47 | '!<%= yo.dist %>/.git*'
48 | ]
49 | }]
50 | },
51 | server: '.tmp'
52 | },
53 | watch: {
54 | gruntfile: {
55 | files: '<%= jshint.gruntfile.src %>',
56 | tasks: ['jshint:gruntfile']
57 | },
58 | less: {
59 | files: ['<%= yo.src %>/{,*/}*.less'],
60 | tasks: ['less:dist']
61 | },
62 | app: {
63 | files: [
64 | '<%= yo.src %>/{,*/}*.html',
65 | '{.tmp,<%= yo.src %>}/{,*/}*.css',
66 | '{.tmp,<%= yo.src %>}/{,*/}*.js'
67 | ],
68 | options: {
69 | livereload: yoConfig.livereload
70 | }
71 | },
72 | test: {
73 | files: '<%= jshint.test.src %>',
74 | tasks: ['jshint:test', 'qunit']
75 | }
76 | },
77 | connect: {
78 | options: {
79 | port: 9000,
80 | hostname: '0.0.0.0' // Change this to '0.0.0.0' to access the server from outside.
81 | },
82 | livereload: {
83 | options: {
84 | middleware: function (connect) {
85 | return [
86 | lrSnippet,
87 | mountFolder(connect, '.tmp'),
88 | mountFolder(connect, yoConfig.src)
89 | ];
90 | }
91 | }
92 | }
93 | },
94 | less: {
95 | options: {
96 | // dumpLineNumbers: 'all',
97 | paths: ['<%= yo.src %>']
98 | },
99 | dist: {
100 | files: {
101 | '<%= yo.src %>/<%= yo.name %>.css': '<%= yo.src %>/<%= yo.name %>.less'
102 | }
103 | }
104 | },
105 | jshint: {
106 | gruntfile: {
107 | options: {
108 | jshintrc: '.jshintrc'
109 | },
110 | src: 'Gruntfile.js'
111 | },
112 | src: {
113 | options: {
114 | jshintrc: '.jshintrc'
115 | },
116 | src: ['<%= yo.src %>/*.js']
117 | },
118 | test: {
119 | options: {
120 | jshintrc: 'test/.jshintrc'
121 | },
122 | src: ['test/**/*.js']
123 | }
124 | },
125 | karma: {
126 | options: {
127 | configFile: 'karma.conf.js',
128 | browsers: ['PhantomJS']
129 | },
130 | unit: {
131 | singleRun: true
132 | },
133 | server: {
134 | autoWatch: true
135 | }
136 | },
137 | ngmin: {
138 | options: {
139 | banner: '<%= meta.banner %>'
140 | },
141 | dist: {
142 | src: ['<%= yo.src %>/*.js'],
143 | dest: '<%= yo.dist %>/<%= pkg.name %>.js'
144 | }
145 | },
146 | concat: {
147 | options: {
148 | banner: '<%= meta.banner %>',
149 | stripBanners: true
150 | },
151 | dist: {
152 | src: ['<%= yo.src %>/<%= pkg.name %>.js'],
153 | dest: '<%= yo.dist %>/<%= pkg.name %>.js'
154 | }
155 | },
156 | uglify: {
157 | options: {
158 | banner: '<%= meta.banner %>'
159 | },
160 | dist: {
161 | src: '<%= concat.dist.dest %>',
162 | dest: '<%= yo.dist %>/<%= pkg.name %>.min.js'
163 | }
164 | },
165 | bump: {
166 | options: {
167 | files: ['package.json', 'bower.json'],
168 | commitMessage: 'chore(release): bump v<%= pkg.version %>',
169 | tagName: 'v<%= pkg.version %>',
170 | tagMessage: 'chore(release): bump v<%= pkg.version %>',
171 | commitFiles: ['-a'],
172 | pushTo: 'github'
173 | }
174 | }
175 | });
176 |
177 | grunt.registerTask('test', [
178 | 'jshint',
179 | 'karma:unit'
180 | ]);
181 |
182 | grunt.registerTask('build', [
183 | 'clean:dist',
184 | 'less:dist',
185 | 'ngmin:dist',
186 | 'uglify:dist'
187 | ]);
188 |
189 | grunt.registerTask('release', [
190 | 'test',
191 | 'bump-only',
192 | 'dist',
193 | 'bump-commit'
194 | ]);
195 |
196 | grunt.registerTask('default', ['build']);
197 |
198 | };
199 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # bootstrap.affix
2 |
3 | Pure AngularJS component replicating [Twitter Bootstrap's Affix](http://getbootstrap.com/javascript/#affix) component behavior.
4 | The affix behavior enables dynamic pinning of a DOM element during page scrolling when specific conditions are met.
5 |
6 | ## Getting Started
7 |
8 | + Install with bower, `bower install angular-bootstrap-affix --save`
9 |
10 | + Or download the [production version][min] or the [development version][max].
11 |
12 | [min]: https://raw.github.com/mgcrea/jquery-bootstrap-affix/master/dist/angular-bootstrap-affix.min.js
13 | [max]: https://raw.github.com/mgcrea/jquery-bootstrap-affix/master/dist/angular-bootstrap-affix.js
14 |
15 | In your web page:
16 |
17 | ```html
18 |
19 |
20 |
21 |
22 | ```
23 |
24 | In your app.js:
25 |
26 | ```js
27 | angular.module('myApp', ['mgcrea.bootstrap.affix'])
28 | ```
29 |
30 | ## Documentation
31 |
32 | + To easily add affix behavior to any element, just add `bs-affix` to the element you want to spy on. Then use offsets to define when to toggle the pinning of an element on and off.
33 |
34 | + Check [Twitter Bootstrap's Affix](http://getbootstrap.com/javascript/#affix) docs.
35 |
36 | ## Examples
37 |
38 | ```html
39 |
43 |
44 | .affix-top {
45 | top: 30px; // css to define
46 | }
47 |
48 | .affix-bottom {
49 | bottom: 0; // css to define
50 | }
51 | ```
52 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-affix",
3 | "version": "0.2.2",
4 | "ignore": [],
5 | "main": [
6 | "dist/angular-bootstrap-affix.js"
7 | ],
8 | "dependencies": {
9 | "angular-jquery": "~0.2.1"
10 | },
11 | "devDependencies": {
12 | "angular": "latest",
13 | "angular-mocks": "latest"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/dist/angular-bootstrap-affix.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Firefox document.body.scrollHeight = 0;
5 | * Because of that the affix-bottom class is constantly added and removed causing flicker.
6 | * This function fixes that.
7 | * Stolen from here: http://james.padolsey.com/snippets/get-document-height-cross-browser/
8 | */
9 | function getDocHeight() {
10 | var D = document;
11 | return Math.max(
12 | D.body.scrollHeight, D.documentElement.scrollHeight,
13 | D.body.offsetHeight, D.documentElement.offsetHeight,
14 | D.body.clientHeight, D.documentElement.clientHeight
15 | );
16 | }
17 |
18 | angular.module('mgcrea.bootstrap.affix', ['mgcrea.jquery']).directive('bsAffix', [
19 | '$window',
20 | 'dimensions',
21 | function ($window, dimensions) {
22 | var checkPosition = function (instance, el, options) {
23 |
24 | var scrollTop = window.pageYOffset;
25 | var windowHeight = window.innerHeight;
26 | var scrollHeight = getDocHeight();///document.body.scrollHeight;
27 | var position = dimensions.offset.call(el[0]);
28 | var height = dimensions.height.call(el[0]);
29 | var offsetTop = options.offsetTop * 1;
30 | var offsetBottom = options.offsetBottom * 1;
31 | var reset = 'affix affix-top affix-bottom';
32 | var affix;
33 | if (instance.originTop == null) {
34 | instance.originTop = position.top;
35 | }
36 | if (windowHeight >= height && instance.originTop <= scrollTop) {
37 | affix = 'top';
38 | } else if (windowHeight <= height && scrollTop >= instance.originTop) {
39 | affix = 'bottom';
40 | } else {
41 | affix = false;
42 | }
43 | if (instance.affixed === affix)
44 | return;
45 | instance.affixed = affix;
46 | el.removeClass(reset).addClass('' + (affix ? 'affix affix-' + affix : ''));
47 | };
48 | var checkCallbacks = function (scope, instance, iElement, iAttrs) {
49 | if (instance.affixed) {
50 | if (iAttrs.onUnaffix)
51 | eval('scope.' + iAttrs.onUnaffix);
52 | } else {
53 | if (iAttrs.onAffix)
54 | eval('scope.' + iAttrs.onAffix);
55 | }
56 | };
57 | return {
58 | restrict: 'EAC',
59 | link: function postLink(scope, iElement, iAttrs) {
60 | var instance = { unpin: null };
61 | //The binding element must be "scrollable"
62 | angular.element($window).bind('scroll', function () {
63 | checkPosition(instance, iElement, iAttrs);
64 | checkCallbacks(scope, instance, iElement, iAttrs);
65 | });
66 | angular.element($window).bind('click', function () {
67 | setTimeout(function () {
68 | checkPosition(instance, iElement, iAttrs);
69 | checkCallbacks(scope, instance, iElement, iAttrs);
70 | }, 1);
71 | });
72 | }
73 | };
74 | }
75 | ]);
--------------------------------------------------------------------------------
/dist/angular-bootstrap-affix.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * angular-bootstrap-affix
3 | * @version v0.2.2 - 2016-08-11
4 | * @link https://github.com/maxisam/angular-bootstrap-affix
5 | * @author Olivier Louvignes (original), maxisam
6 | * @license MIT License, http://www.opensource.org/licenses/MIT
7 | */
8 | "use strict";angular.module("mgcrea.bootstrap.affix",["mgcrea.jquery"]).directive("bsAffix",["$window","dimensions",function($window,dimensions){var checkPosition=function(a,b,c){var d,e=window.pageYOffset,f=window.innerHeight,g=(document.body.scrollHeight,dimensions.offset.call(b[0])),h=dimensions.height.call(b[0]),i=(1*c.offsetTop,1*c.offsetBottom,"affix affix-top affix-bottom");null==a.originTop&&(a.originTop=g.top),d=f>=h&&a.originTop<=e?"top":h>=f&&e>=a.originTop?"bottom":!1,a.affixed!==d&&(a.affixed=d,b.removeClass(i).addClass(""+(d?"affix affix-"+d:"")))},checkCallbacks=function(scope,instance,iElement,iAttrs){instance.affixed?iAttrs.onUnaffix&&eval("scope."+iAttrs.onUnaffix):iAttrs.onAffix&&eval("scope."+iAttrs.onAffix)};return{restrict:"EAC",link:function(a,b,c){var d={unpin:null};angular.element($window).bind("scroll",function(){checkPosition(d,b,c),checkCallbacks(a,d,b,c)}),angular.element($window).bind("click",function(){setTimeout(function(){checkPosition(d,b,c),checkCallbacks(a,d,b,c)},1)})}}}]);
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 |
3 | // base path, that will be used to resolve files and exclude
4 | basePath = '';
5 |
6 | // list of files / patterns to load in the browser
7 | files = [
8 | JASMINE,
9 | JASMINE_ADAPTER,
10 | 'bower_components/angular/angular.js',
11 | 'bower_components/jquery/jquery.js',
12 | 'bower_components/angular-mocks/angular-mocks.js',
13 | 'src/*.js',
14 | 'test/spec/*.js'
15 | ];
16 |
17 | // list of files to exclude
18 | exclude = [];
19 |
20 | // test results reporter to use
21 | // possible values: dots || progress || growl
22 | reporters = ['progress'];
23 |
24 | // web server port
25 | port = 8080;
26 |
27 | // cli runner port
28 | runnerPort = 9100;
29 |
30 | // enable / disable colors in the output (reporters and logs)
31 | colors = true;
32 |
33 | // level of logging
34 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
35 | logLevel = LOG_INFO;
36 |
37 | // enable / disable watching file and executing tests whenever any file changes
38 | autoWatch = false;
39 |
40 | // Start these browsers, currently available:
41 | // - Chrome
42 | // - ChromeCanary
43 | // - Firefox
44 | // - Opera
45 | // - Safari (only Mac)
46 | // - PhantomJS
47 | // - IE (only Windows)
48 | browsers = ['Chrome'];
49 |
50 | // If browser does not capture in given timeout [ms], kill it
51 | captureTimeout = 5000;
52 |
53 | // Continuous Integration mode
54 | // if true, it capture browsers, run tests and exit
55 | singleRun = false;
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-bootstrap-affix",
3 | "version": "0.2.2",
4 | "description": "bootstrap-affix",
5 | "keywords": [
6 | "angular"
7 | ],
8 | "homepage": "https://github.com/maxisam/angular-bootstrap-affix",
9 | "bugs": "https://github.com/maxisam/angular-bootstrap-affix",
10 | "author": {
11 | "name": "Olivier Louvignes (original), maxisam",
12 | "email": "maxisam@gmail.com",
13 | "url": "https://github.com/mgcrea"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "https://github.com/mgcrea/bootstrap-affix.git"
18 | },
19 | "licenses": [
20 | {
21 | "type": "MIT"
22 | }
23 | ],
24 | "devDependencies": {
25 | "grunt": "~0.4.1",
26 | "grunt-contrib-jshint": "~0.3.0",
27 | "grunt-contrib-concat": "~0.3.0",
28 | "grunt-contrib-uglify": "~0.2.2",
29 | "grunt-contrib-watch": "~0.4.4",
30 | "grunt-contrib-clean": "~0.4.1",
31 | "grunt-contrib-connect": "~0.3.0",
32 | "grunt-contrib-less": "~0.6.1",
33 | "connect-livereload": "~0.2.0",
34 | "grunt-open": "~0.2.0",
35 | "grunt-karma": "~0.4.5",
36 | "grunt-ngmin": "0.0.3",
37 | "grunt-bump": "~0.0.10",
38 | "matchdep": "~0.1.2"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/bootstrap-affix.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | angular
4 | .module("mgcrea.bootstrap.affix", ["mgcrea.jquery"])
5 | .directive("bsAffix", function($window, dimensions) {
6 | var checkPosition = function(instance, el, options) {
7 | var scrollTop = window.pageYOffset;
8 | var windowHeight = window.innerHeight;
9 | var scrollHeight = document.body.scrollHeight;
10 | var position = dimensions.offset.call(el[0]);
11 | var height = dimensions.height.call(el[0]);
12 | var offsetTop = options.offsetTop * 1;
13 | var offsetBottom = options.offsetBottom * 1;
14 | var reset = "affix affix-top affix-bottom";
15 | var affix;
16 | if (instance.originTop == null) {
17 | instance.originTop = position.top;
18 | }
19 | if (windowHeight >= height && instance.originTop <= scrollTop) {
20 | affix = "top";
21 | } else if (windowHeight <= height && scrollTop >= instance.originTop) {
22 | affix = "bottom";
23 | } else {
24 | affix = false;
25 | }
26 | if (instance.affixed === affix) return;
27 | instance.affixed = affix;
28 |
29 | el
30 | .removeClass(reset)
31 | .addClass("" + (affix ? "affix affix-" + affix : ""));
32 | };
33 |
34 | var checkCallbacks = function(scope, instance, iElement, iAttrs) {
35 | if (instance.affixed) {
36 | if (iAttrs.onUnaffix) eval("scope." + iAttrs.onUnaffix);
37 | } else {
38 | if (iAttrs.onAffix) eval("scope." + iAttrs.onAffix);
39 | }
40 | };
41 |
42 | return {
43 | restrict: "EAC",
44 | link: function postLink(scope, iElement, iAttrs) {
45 | var instance = { unpin: null };
46 |
47 | angular.element($window).bind("scroll", function() {
48 | checkPosition(instance, iElement, iAttrs);
49 | checkCallbacks(scope, instance, iElement, iAttrs);
50 | });
51 |
52 | angular.element($window).bind("click", function() {
53 | setTimeout(function() {
54 | checkPosition(instance, iElement, iAttrs);
55 | checkCallbacks(scope, instance, iElement, iAttrs);
56 | }, 1);
57 | });
58 | }
59 | };
60 | });
61 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": false,
7 | "curly": false,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": false,
18 | "strict": true,
19 | "globalstrict": true,
20 | "trailing": true,
21 | "smarttabs": true,
22 | "predef": [
23 | "$",
24 | "angular",
25 | "describe",
26 | "beforeEach",
27 | "afterEach",
28 | "inject",
29 | "it",
30 | "expect"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/test/spec/bootstrap.affix.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Module: bootstrap.affix', function () {
4 | var scope, $sandbox, $compile, $timeout;
5 |
6 | // load the controller's module
7 | beforeEach(module('mgcrea.bootstrap.affix'));
8 |
9 | beforeEach(inject(function ($injector, $rootScope, _$compile_, _$timeout_) {
10 | scope = $rootScope;
11 | $compile = _$compile_;
12 | $timeout = _$timeout_;
13 |
14 | $sandbox = $('').appendTo($('body'));
15 | }));
16 |
17 | afterEach(function() {
18 | $sandbox.remove();
19 | scope.$destroy();
20 | });
21 |
22 | var templates = {
23 | 'default': {
24 | scope: {},
25 | element: ''
26 | }
27 | };
28 |
29 | function compileDirective(template) {
30 | template = template ? templates[template] : templates['default'];
31 | angular.extend(scope, template.scope || templates['default'].scope);
32 | var $element = $(template.element).appendTo($sandbox);
33 | $element = $compile($element)(scope);
34 | scope.$digest();
35 | return $element;
36 | }
37 |
38 | it('should correctly display hello world', function () {
39 | var elm = compileDirective();
40 | expect(elm.text()).toBe('hello world');
41 | });
42 |
43 | });
44 |
--------------------------------------------------------------------------------