From a5067e6bab7a353e9fd740bd5127077569dc5186 Mon Sep 17 00:00:00 2001 From: Jay Traband Date: Sun, 3 Jan 2016 11:32:57 -0800 Subject: [PATCH] chore(testing): e2e testing improvements; more devguide sample tests closes #643 --- gulpfile.js | 45 ++++-- .../docs/_examples/architecture/e2e-spec.js | 3 +- public/docs/_examples/forms/e2e-spec.js | 3 +- .../e2e-spec.js | 3 +- .../_examples/lifecycle-hooks/e2e-spec.js | 14 +- public/docs/_examples/pipes/e2e-spec.js | 10 +- public/docs/_examples/protractor.config.js | 10 ++ public/docs/_examples/router/e2e-spec.js | 128 ++++++++++++++++++ public/docs/_examples/tutorial/e2e-spec.js | 105 +++++++++++--- public/docs/_examples/user-input/e2e-spec.js | 101 ++++++++++++++ 10 files changed, 370 insertions(+), 52 deletions(-) create mode 100644 public/docs/_examples/router/e2e-spec.js create mode 100644 public/docs/_examples/user-input/e2e-spec.js diff --git a/gulpfile.js b/gulpfile.js index 46f9753d05..e60ab8a441 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -59,7 +59,7 @@ var _excludeMatchers = _excludePatterns.map(function(excludePattern){ return new Minimatch(excludePattern) }); -var _exampleBoilerplateFiles = ['package.json', 'tsconfig.json', 'karma.conf.js', 'karma-test-shim.js' ] +var _exampleBoilerplateFiles = ['package.json', 'tsconfig.json', 'karma.conf.js', 'karma-test-shim.js' ]; // --filter may be passed in to filter/select _example app subdir names // i.e. gulp run-e2e-tests --filter=foo ; would select all example apps with @@ -71,8 +71,10 @@ gulp.task('run-e2e-tests', function() { var exePath = path.join(process.cwd(), "./node_modules/.bin/"); spawnInfo = spawnExt('webdriver-manager', ['update'], {cwd: exePath}); return spawnInfo.promise; - }).then(function(x) { + }).then(function() { return findAndRunE2eTests(argv.filter); + }).then(function(status) { + reportStatus(status); }).fail(function(e) { return e; }); @@ -82,6 +84,7 @@ gulp.task('run-e2e-tests', function() { // with the corresponding apps that they should run under. Then run // each app/spec collection sequentially. function findAndRunE2eTests(filter) { + var startTime = new Date().getTime(); // create an output file with header. var outputFile = path.join(process.cwd(), 'protractor-results.txt'); var header = "Protractor example results for: " + (new Date()).toLocaleString() + "\n\n"; @@ -111,11 +114,20 @@ function findAndRunE2eTests(filter) { }); // run the tests sequentially + var status = { passed: [], failed: [] }; return exeConfigs.reduce(function (promise, combo) { return promise.then(function () { - return runE2eTests(combo.examplePath, combo.protractorConfigFilename, outputFile); + return runE2eTests(combo.examplePath, combo.protractorConfigFilename, outputFile).then(function(ok) { + var arr = ok ? status.passed : status.failed; + arr.push(combo.examplePath); + }) }); - }, Q.resolve()); + }, Q.resolve()).then(function() { + var stopTime = new Date().getTime(); + status.elapsedTime = (stopTime - startTime)/1000; + fs.appendFileSync(outputFile, '\nElaped Time: ' + status.elapsedTime + ' seconds'); + return status; + }); } // start the example in appDir; then run protractor with the specified @@ -132,18 +144,35 @@ function runE2eTests(appDir, protractorConfigFilename, outputFile ) { [ pcFilename, '--params.appDir=' + appDir, '--params.outputFile=' + outputFile], { cwd: exePath }); return spawnInfo.promise.then(function(data) { // kill the app now that protractor has completed. - treeKill(appRunSpawnInfo.proc.pid); // Ugh... proc.kill does not work properly on windows with child processes. // appRun.proc.kill(); - return data; + treeKill(appRunSpawnInfo.proc.pid); + return true; }).fail(function(err) { // Ugh... proc.kill does not work properly on windows with child processes. // appRun.proc.kill(); - treeKill(appRunSpawnInfo.proc.pid) - return err; + treeKill(appRunSpawnInfo.proc.pid); + return false; }); } +function reportStatus(status) { + gutil.log('Suites passed:'); + status.passed.forEach(function(val) { + gutil.log(' ' + val); + }); + + gutil.log('Suites failed:'); + status.failed.forEach(function(val) { + gutil.log(' ' + val); + }); + + if (status.failed.length == 0) { + gutil.log('All tests passed'); + } + gutil.log('Elapsed time: ' + status.elapsedTime + ' seconds'); +} + // returns both a promise and the spawned process so that it can be killed if needed. function spawnExt(command, args, options) { var deferred = Q.defer(); diff --git a/public/docs/_examples/architecture/e2e-spec.js b/public/docs/_examples/architecture/e2e-spec.js index 1be545fa6c..a8818ad019 100644 --- a/public/docs/_examples/architecture/e2e-spec.js +++ b/public/docs/_examples/architecture/e2e-spec.js @@ -46,7 +46,8 @@ describe('Architecture', function () { // check that both the initial selected item and the detail title reflect changes // made to the input box. - heroNameEle.sendKeys('foo'); + // heroNameEle.sendKeys('foo'); + sendKeys(heroNameEle, 'foo'); expect(detailTitleEle.getText()).toContain('foo'); expect(selectEle.getText()).toContain('foo'); diff --git a/public/docs/_examples/forms/e2e-spec.js b/public/docs/_examples/forms/e2e-spec.js index 093b170688..e7714cb2c4 100644 --- a/public/docs/_examples/forms/e2e-spec.js +++ b/public/docs/_examples/forms/e2e-spec.js @@ -44,7 +44,8 @@ describeIf(browser.appIsTs, 'Forms Tests', function () { var newValue; var alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0); alterEgoEle.getAttribute('value').then(function(value) { - alterEgoEle.sendKeys(test); + // alterEgoEle.sendKeys(test); + sendKeys(alterEgoEle, test); newValue = value + test; expect(alterEgoEle.getAttribute('value')).toEqual(newValue); }).then(function() { diff --git a/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.js b/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.js index df867c8a62..2f63c32d52 100644 --- a/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.js +++ b/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.js @@ -37,7 +37,8 @@ describe('Hierarchical dependency injection', function () { var editButtonEle = heroEle.element(by.cssContainingText('button','edit')); editButtonEle.click().then(function() { inputEle = heroEle.element(by.css('hero-editor input')); - return inputEle.sendKeys("foo"); + // return inputEle.sendKeys("foo"); + return sendKeys(inputEle, "foo"); }).then(function() { buttonName = shouldSave ? 'save' : 'cancel'; var buttonEle = heroEle.element(by.cssContainingText('button', buttonName)); diff --git a/public/docs/_examples/lifecycle-hooks/e2e-spec.js b/public/docs/_examples/lifecycle-hooks/e2e-spec.js index 13c0421737..d201fd7bfb 100644 --- a/public/docs/_examples/lifecycle-hooks/e2e-spec.js +++ b/public/docs/_examples/lifecycle-hooks/e2e-spec.js @@ -38,7 +38,8 @@ describe('Lifecycle hooks', function () { expect(titleEle.getText()).toContain('Windstorm can sing'); var changeLogEles = onChangesViewEle.all(by.css('div')); expect(changeLogEles.count()).toEqual(3, "should start with 3 messages"); - heroNameInputEle.sendKeys('-foo-').then(function () { + // heroNameInputEle.sendKeys('-foo-').then(function () { + sendKeys(heroNameInputEle, '-foo-').then(function () { expect(titleEle.getText()).toContain('Windstorm-foo- can sing'); expect(changeLogEles.count()).toEqual(3, "should still have 3 messages"); // protractor bug with sendKeys means that line below does not work. @@ -140,15 +141,4 @@ describe('Lifecycle hooks', function () { }); - // Hack - because of bug with send keys - function sendKeys(element, str) { - return str.split('').reduce(function (promise, char) { - return promise.then(function () { - return element.sendKeys(char); - }); - }, element.getAttribute('value')); - // better to create a resolved promise here but ... don't know how with protractor; - } - - }); diff --git a/public/docs/_examples/pipes/e2e-spec.js b/public/docs/_examples/pipes/e2e-spec.js index 2a4318d162..a8637947d0 100644 --- a/public/docs/_examples/pipes/e2e-spec.js +++ b/public/docs/_examples/pipes/e2e-spec.js @@ -65,14 +65,6 @@ describe('Pipes', function () { }); }); - // Hack - because of bug with send keys - function sendKeys(element, str) { - return str.split('').reduce(function (promise, char) { - return promise.then(function () { - return element.sendKeys(char); - }); - }, element.getAttribute('value')); - // better to create a resolved promise here but ... don't know how with protractor; - } + }); diff --git a/public/docs/_examples/protractor.config.js b/public/docs/_examples/protractor.config.js index 217c3a08e2..c5bc91b998 100644 --- a/public/docs/_examples/protractor.config.js +++ b/public/docs/_examples/protractor.config.js @@ -58,6 +58,7 @@ exports.config = { jasmine.getEnv().addReporter(new Reporter( browser.params )) ; global.describeIf = describeIf; global.itIf = itIf; + global.sendKeys = sendKeys; }, jasmineNodeOpts: { @@ -84,6 +85,15 @@ function itIf(cond, name, func) { } } +// Hack - because of bug with send keys +function sendKeys(element, str) { + return str.split('').reduce(function (promise, char) { + return promise.then(function () { + return element.sendKeys(char); + }); + }, element.getAttribute('value')); + // better to create a resolved promise here but ... don't know how with protractor; +} function Reporter(options) { diff --git a/public/docs/_examples/router/e2e-spec.js b/public/docs/_examples/router/e2e-spec.js new file mode 100644 index 0000000000..0cea6a73fb --- /dev/null +++ b/public/docs/_examples/router/e2e-spec.js @@ -0,0 +1,128 @@ +describe('Router', function () { + + beforeAll(function () { + browser.get(''); + }); + + function getPageStruct() { + hrefEles = element.all(by.css('my-app a')); + + return { + hrefs: hrefEles, + routerParent: element(by.css('my-app > undefined')), + routerTitle: element(by.css('my-app > undefined > h2')), + + crisisHref: hrefEles.get(0), + crisisList: element.all(by.css('my-app > undefined > undefined li')), + crisisDetail: element(by.css('my-app > undefined > undefined > div')), + crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')), + + heroesHref: hrefEles.get(1), + heroesList: element.all(by.css('my-app > undefined li')), + heroDetail: element(by.css('my-app > undefined > div')), + heroDetailTitle: element(by.css('my-app > undefined > div > h3')), + + } + } + + it('should be able to see the start screen', function () { + var page = getPageStruct(); + expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); + expect(page.crisisHref.getText()).toEqual("Crisis Center"); + expect(page.heroesHref.getText()).toEqual("Heroes"); + }); + + // assumes that jasmine runs tests in order that they appear. + // (don't move this test later in this file because it will fail first 'expect'). + it('should be able to see crises center items', function () { + var page = getPageStruct(); + expect(page.crisisList.count()).toBe(0, "should be no crisis center entries on startup"); + page.crisisHref.click().then(function() { + expect(page.routerTitle.getText()).toContain('CRISIS CENTER'); + expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries"); + }); + }); + + it('should be able to see hero items', function () { + var page = getPageStruct(); + page.heroesHref.click().then(function() { + expect(page.routerTitle.getText()).toContain('HEROES'); + expect(page.heroesList.count()).toBe(6, "should be 6 heroes"); + }); + }); + + it('should be able to toggle the views', function () { + var page = getPageStruct(); + page.crisisHref.click().then(function() { + expect(page.crisisList.count()).toBe(4, "should be 4 crisis center entries"); + return page.heroesHref.click(); + }).then(function() { + expect(page.heroesList.count()).toBe(6, "should be 6 heroes"); + }); + }); + + it('should be able to edit and save details from the crisis center view', function () { + crisisCenterEdit(2, true); + }); + + it('should be able to edit and cancel details from the crisis center view', function () { + crisisCenterEdit(3, false); + }); + + it('should be able to edit and save details from the heroes view', function () { + var page = getPageStruct(); + var heroEle, heroText; + page.heroesHref.click().then(function() { + heroEle = page.heroesList.get(4); + return heroEle.getText(); + }).then(function(text) { + expect(text.length).toBeGreaterThan(0, 'should have some text'); + // remove leading id from text + heroText = text.substr(text.indexOf(' ')).trim(); + return heroEle.click(); + }).then(function() { + expect(page.heroesList.count()).toBe(0, "should no longer see crisis center entries"); + expect(page.heroDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); + expect(page.heroDetailTitle.getText()).toContain(heroText); + var inputEle = page.heroDetail.element(by.css('input')); + return sendKeys(inputEle, '-foo'); + }).then(function() { + expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo'); + var buttonEle = page.heroDetail.element(by.css('button')); + return buttonEle.click(); + }).then(function() { + expect(heroEle.getText()).toContain(heroText + '-foo'); + }) + }); + + function crisisCenterEdit(index, shouldSave) { + var page = getPageStruct(); + var crisisEle, crisisText; + page.crisisHref.click().then(function () { + crisisEle = page.crisisList.get(index); + return crisisEle.getText(); + }).then(function (text) { + expect(text.length).toBeGreaterThan(0, 'should have some text'); + // remove leading id from text + crisisText = text.substr(text.indexOf(' ')).trim(); + return crisisEle.click(); + }).then(function () { + expect(page.crisisList.count()).toBe(0, "should no longer see crisis center entries"); + expect(page.crisisDetail.isPresent()).toBe(true, 'should be able to see crisis detail'); + expect(page.crisisDetailTitle.getText()).toContain(crisisText); + var inputEle = page.crisisDetail.element(by.css('input')); + return sendKeys(inputEle, '-foo'); + }).then(function () { + expect(page.crisisDetailTitle.getText()).toContain(crisisText + '-foo'); + var buttonEle = page.crisisDetail.element(by.cssContainingText('button', shouldSave ? 'Save' : 'Cancel')); + return buttonEle.click(); + }).then(function () { + if (shouldSave) { + expect(crisisEle.getText()).toContain(crisisText + '-foo'); + } else { + expect(crisisEle.getText()).not.toContain(crisisText + '-foo'); + } + }); + } + +}); diff --git a/public/docs/_examples/tutorial/e2e-spec.js b/public/docs/_examples/tutorial/e2e-spec.js index eec022ed23..f35fcd1287 100644 --- a/public/docs/_examples/tutorial/e2e-spec.js +++ b/public/docs/_examples/tutorial/e2e-spec.js @@ -1,46 +1,111 @@ -// Not yet complete describe('Tutorial', function () { beforeAll(function () { browser.get(''); - init(); }); - var _hrefEles, _tohDashboardHrefEle, _tohHeroesHrefEle; + function getPageStruct() { + hrefEles = element.all(by.css('my-app a')); - function init() { - _hrefEles = element.all(by.css('my-app a')); + return { + hrefs: hrefEles, + myDashboardHref: hrefEles.get(0), + myDashboardParent: element(by.css('my-app my-dashboard')), + topHeroes: element.all(by.css('my-app my-dashboard .module.hero')), - _tohDashboardHrefEle = _hrefEles.get(0); - _tohHeroesHrefEle = _hrefEles.get(1); + myHeroesHref: hrefEles.get(1), + myHeroesParent: element(by.css('my-app my-heroes')), + allHeroes: element.all(by.css('my-app my-heroes li')), + heroDetail: element(by.css('my-app my-hero-detail')) + } } it('should be able to see the start screen', function () { - expect(_hrefEles.count()).toEqual(2, 'should be two dashboard choices'); - expect(_tohDashboardHrefEle.getText()).toEqual("Dashboard"); - expect(_tohHeroesHrefEle.getText()).toEqual("Heroes"); + var page = getPageStruct(); + expect(page.hrefs.count()).toEqual(2, 'should be two dashboard choices'); + expect(page.myDashboardHref.getText()).toEqual("Dashboard"); + expect(page.myHeroesHref.getText()).toEqual("Heroes"); }); it('should be able to see dashboard choices', function () { - var dashboardHeroEles = element.all(by.css('my-app my-dashboard .module.hero')); - expect(dashboardHeroEles.count()).toBe(4, "should be 4 dashboard hero choices"); + var page = getPageStruct(); + expect(page.topHeroes.count()).toBe(4, "should be 4 dashboard hero choices"); }); it('should be able to toggle the views', function () { - var dashboardEle = element(by.css('my-app my-dashboard')); - expect(dashboardEle.element(by.css('h3')).getText()).toEqual('Top Heroes'); - _tohHeroesHrefEle.click().then(function() { - expect(dashboardEle.isPresent()).toBe(false, 'should no longer see dashboard element'); - var heroEles = element.all(by.css('my-app my-heroes li')); - expect(heroEles.count()).toBeGreaterThan(4, "should be more than 4 heroes shown"); - return _tohDashboardHrefEle.click(); + var page = getPageStruct(); + + expect(page.myDashboardParent.element(by.css('h3')).getText()).toEqual('Top Heroes'); + page.myHeroesHref.click().then(function() { + expect(page.myDashboardParent.isPresent()).toBe(false, 'should no longer see dashboard element'); + expect(page.allHeroes.count()).toBeGreaterThan(4, "should be more than 4 heroes shown"); + return page.myDashboardHref.click(); }).then(function() { - expect(dashboardEle.isPresent()).toBe(true, 'should once again see the dashboard element'); + expect(page.myDashboardParent.isPresent()).toBe(true, 'should once again see the dashboard element'); }); }); + it('should be able to edit details from "Dashboard" view', function () { + var page = getPageStruct(); + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be available'); + var heroEle = page.topHeroes.get(3); + var heroDescrEle = heroEle.element(by.css('h4')); + var heroDescr; + return heroDescrEle.getText().then(function(text) { + heroDescr = text; + return heroEle.click(); + }).then(function() { + return editDetails(page, heroDescr, '-foo'); + }).then(function() { + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be back'); + expect(heroDescrEle.getText()).toEqual(heroDescr + '-foo'); + }); + }); + + it('should be able to edit details from "Heroes" view', function () { + var page = getPageStruct(); + expect(page.myDashboardParent.isPresent()).toBe(true, 'dashboard element should be present'); + var viewDetailsButtonEle = page.myHeroesParent.element(by.cssContainingText('button', 'View Details')); + var heroEle, heroDescr; + page.myHeroesHref.click().then(function() { + expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); + expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be present'); + expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should not yet be present'); + heroEle = page.allHeroes.get(2); + return heroEle.getText(); + }).then(function(text) { + // remove leading 'id' from the element + heroDescr = text.substr(text.indexOf(' ')+1); + return heroEle.click(); + }).then(function() { + expect(viewDetailsButtonEle.isDisplayed()).toBe(true, 'viewDetails button should now be visible'); + return viewDetailsButtonEle.click(); + }).then(function() { + return editDetails(page, heroDescr, '-bar'); + }).then(function() { + expect(page.myHeroesParent.isPresent()).toBe(true, 'myHeroes element should be back'); + expect(heroEle.getText()).toContain(heroDescr + '-bar'); + expect(viewDetailsButtonEle.isPresent()).toBe(false, 'viewDetails button should again NOT be present'); + }); + }); + + function editDetails(page, origValue, textToAdd) { + expect(page.myDashboardParent.isPresent()).toBe(false, 'dashboard element should NOT be present'); + expect(page.myHeroesParent.isPresent()).toBe(false, 'myHeroes element should NOT be present'); + expect(page.heroDetail.isDisplayed()).toBe(true, 'should be able to see hero-details'); + var inputEle = page.heroDetail.element(by.css('input')); + expect(inputEle.isDisplayed()).toBe(true, 'should be able to see the input box'); + var backButtonEle = page.heroDetail.element(by.css('button')); + expect(backButtonEle.isDisplayed()).toBe(true, 'should be able to see the back button'); + var detailTextEle = page.heroDetail.element(by.css('div h2')); + expect(detailTextEle.getText()).toContain(origValue); + return sendKeys(inputEle, textToAdd).then(function () { + expect(detailTextEle.getText()).toContain(origValue + textToAdd); + return backButtonEle.click(); + }); + } }); diff --git a/public/docs/_examples/user-input/e2e-spec.js b/public/docs/_examples/user-input/e2e-spec.js new file mode 100644 index 0000000000..c0d4fa0c1f --- /dev/null +++ b/public/docs/_examples/user-input/e2e-spec.js @@ -0,0 +1,101 @@ +describe('User Input Tests', function () { + + beforeAll(function () { + browser.get(''); + }); + + it('should support the click event', function () { + var mainEle = element(by.css('click-me')); + var buttonEle =element(by.css('click-me button')); + expect(mainEle.getText()).not.toContain('You are my hero!'); + buttonEle.click().then(function() { + expect(mainEle.getText()).toContain('You are my hero!'); + }); + }); + + it('should support the click event with an event payload', function () { + var mainEle = element(by.css('click-me2')); + var buttonEle =element(by.css('click-me2 button')); + expect(mainEle.getText()).not.toContain('Event target is '); + buttonEle.click().then(function() { + expect(mainEle.getText()).toContain('Event target is BUTTON'); + }) + }); + + it('should support the keyup event ', function () { + var mainEle = element(by.css('key-up1')); + var inputEle = mainEle.element(by.css('input')); + var outputTextEle = mainEle.element(by.css('p')); + expect(outputTextEle.getText()).toEqual(''); + return sendKeys(inputEle,'abc').then(function() { + expect(outputTextEle.getText()).toEqual('a | ab | abc |'); + }); + }); + + it('should support user input from a local template var (loopback)', function () { + var mainEle = element(by.css('loop-back')); + var inputEle = mainEle.element(by.css('input')); + var outputTextEle = mainEle.element(by.css('p')); + expect(outputTextEle.getText()).toEqual(''); + return sendKeys(inputEle,'abc').then(function() { + expect(outputTextEle.getText()).toEqual('abc'); + }); + }); + + it('should be able to combine click event with a local template var', function () { + var mainEle = element(by.css('key-up2')); + var inputEle = mainEle.element(by.css('input')); + var outputTextEle = mainEle.element(by.css('p')); + expect(outputTextEle.getText()).toEqual(''); + return sendKeys(inputEle,'abc').then(function() { + expect(outputTextEle.getText()).toEqual('a | ab | abc |'); + }); + }); + + it('should be able to filter key events', function () { + var mainEle = element(by.css('key-up3')); + var inputEle = mainEle.element(by.css('input')); + var outputTextEle = mainEle.element(by.css('p')); + expect(outputTextEle.getText()).toEqual(''); + return sendKeys(inputEle,'abc').then(function() { + expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet'); + return sendKeys(inputEle, protractor.Key.ENTER); + }).then(function() { + expect(outputTextEle.getText()).toEqual('abc'); + }); + }); + + it('should be able to filter blur events', function () { + var prevInputEle = element(by.css('key-up3 input')); + var mainEle = element(by.css('key-up4')); + var inputEle = mainEle.element(by.css('input')); + var outputTextEle = mainEle.element(by.css('p')); + expect(outputTextEle.getText()).toEqual(''); + return sendKeys(inputEle,'abc').then(function() { + expect(outputTextEle.getText()).toEqual('', 'should be blank - have not sent enter yet'); + // change the focus + return prevInputEle.click(); + }).then(function() { + expect(outputTextEle.getText()).toEqual('abc'); + }); + }); + + it('should be able to compose little tour of heroes', function () { + var mainEle = element(by.css('little-tour')); + var inputEle = mainEle.element(by.css('input')); + var addButtonEle = mainEle.element(by.css('button')); + var heroEles = mainEle.all(by.css('li')); + var numHeroes; + expect(heroEles.count()).toBeGreaterThan(0); + heroEles.count().then(function(count) { + numHeroes = count; + return sendKeys(inputEle, 'abc'); + }).then(function() { + return addButtonEle.click(); + }).then(function() { + expect(heroEles.count()).toEqual(numHeroes + 1, 'should be one more hero added'); + expect(heroEles.get(numHeroes).getText()).toContain('abc'); + }); + }); +}); +