{"id":15659618,"url":"https://github.com/xotabu4/protractor-element-extend","last_synced_at":"2026-02-27T11:14:47.145Z","repository":{"id":57331846,"uuid":"72845663","full_name":"Xotabu4/protractor-element-extend","owner":"Xotabu4","description":"Module, that helps you to extend ElementFinder in your own custom fragments","archived":false,"fork":false,"pushed_at":"2023-02-28T07:12:47.000Z","size":102,"stargazers_count":20,"open_issues_count":9,"forks_count":13,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-28T16:21:42.096Z","etag":null,"topics":["fragments","pageobject","pageobject-pattern","protractor","webdriver"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Xotabu4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-04T12:25:55.000Z","updated_at":"2025-03-15T22:31:36.000Z","dependencies_parsed_at":"2024-10-23T06:25:23.752Z","dependency_job_id":null,"html_url":"https://github.com/Xotabu4/protractor-element-extend","commit_stats":{"total_commits":31,"total_committers":6,"mean_commits":5.166666666666667,"dds":"0.25806451612903225","last_synced_commit":"930e23a8fff908fd77a062ea0b72fde338eac64d"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Xotabu4%2Fprotractor-element-extend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Xotabu4%2Fprotractor-element-extend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Xotabu4%2Fprotractor-element-extend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Xotabu4%2Fprotractor-element-extend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Xotabu4","download_url":"https://codeload.github.com/Xotabu4/protractor-element-extend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251342755,"owners_count":21574249,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fragments","pageobject","pageobject-pattern","protractor","webdriver"],"created_at":"2024-10-03T13:17:51.277Z","updated_at":"2026-02-27T11:14:47.116Z","avatar_url":"https://github.com/Xotabu4.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/)\n\n# This library is deprecated and won't be updated in future, since protractor support is ending as well: https://github.com/angular/protractor/issues/5502\n\n[![Build Status](https://travis-ci.org/Xotabu4/protractor-element-extend.svg?branch=master)](https://travis-ci.org/Xotabu4/protractor-element-extend)\n# Protractor Page Fragments\n\nSimple module, that helps you build your own page fragments, that are inherited from ElementFinder/ElementArrayFinder objects, that brings awesome posibilities to your ProtractorJS tests.\n\nYou might heard other names for this pattern: Page Components, Page Composition, HTML Elements, Custom WebElements, WebElement inheritance, Page Elements. This is all about the same.\n\nInstalling\n---------------------\nAs any other NPM package:\n```\nnpm install protractor-element-extend --save-dev\n```\n\nImporting\n----------------------\nJS:\n\n`let BaseFragment = require('protractor-element-extend').BaseFragment`\n\n`let BaseArrayFragment = require('protractor-element-extend').BaseArrayFragment`\n\nTS:\n`import {BaseFragment, BaseArrayFragment} from 'protractor-element-extend'`\n\n\nCreating own single fragment\n----------------------\n\nTo declare your own fragment, declare your class, extend it from BaseFragment, and pass ElementFinder(WebElement in protractorJS) that represents this fragment to super constructor.\n\nHere is example how Checkbox fragment can be declared:\n```typescript\nimport {BaseFragment} from 'protractor-element-extend'\nimport {browser, ExpectedConditions as EC} from 'protractor'\n\nclass Checkbox extends BaseFragment {\n  constructor(element) {\n    super(element) //that's it! now your 'this' reference, is reference to element passed to super constructor\n  }\n  \n  //You can extend it with any methods that you what, and use internal reference to your element\n  select() {\n    this.isSelected().then(selected =\u003e {\n      if(!selected) {\n        this.click()\n        // Notice that because your element is valid ElementFinder - you can pass it as parameter to ExpectedConditions.\n        browser.wait(EC.elementToBeSelected(this), 5000, `Checkbox ${this.locator()} must became selected after click, but it wasn't`)\n      } else {\n          console.warn(`Checkbox ${this.locator()} was already selected, skipping select`)\n      }\n    })\n  }\n  \n  unselect() {\n    this.isSelected().then(selected =\u003e {\n      if(selected) {\n        this.click()\n        // Notice that because your element is valid ElementFinder - you can pass it as parameter to ExpectedConditions!\n        browser.wait(EC.not(EC.elementToBeSelected(this)), 5000, `Checkbox ${this.locator()} must became unselected after click, but it wasn't`)\n      } else {\n          console.warn(`Checkbox ${this.locator()} was already unselected, skipping unselect`)\n      }\n    })\n  }\n\n}\n```\nCreating own collection of custom fragments\n----------------------\nOften needed to work with own custom collection of own fragments, not only single fragment. For this purpose BaseArrayFragment is added. This object extends ElementArrayFragment, and overrides methods that return single elements, to return your custom fragments. `.map() .filter() .reduce() .each()` and other will receive your custom fragment as parameter as well.\n\nHere is example how SearchResultsCollection fragment can be declared:\n```typescript\nimport { BaseArrayFragment, BaseFragment } from 'protractor-element-extend'\nimport { browser, ExpectedConditions as EC, $$ } from 'protractor'\n\n// Describing single search result on our page. Notice that constructor declaration could be skipped, in this case constructor from BaseFragment will be used\nclass SearchResult extends BaseFragment {\n    name() {\n      return $('.name').getText()\n    }\n\n    isDiscounted() {\n        return this.$('.discount-label').isDisplayed()\n    }\n\n    open() {\n        this.$('button.open').click()\n    }\n}\n\n// Generics - \u003cSearchResults\u003e are needs to be defined to provide typings support.\nclass SearchResultsCollection extends BaseArrayFragment\u003cSearchResult\u003e {\n    constructor(elementsToExtend: ElementArrayFinder) {\n        // You should pass ElementArrayFinder into super constructor, and constructor(class) that will be used to wrap each element in your collection\n        super(elementsToExtend, SearchResult);\n    }\n\n    findResultsWithDiscount() {\n        // This will return new SearchResults object with only those SearchResult objects that has isDiscounted == true\n        return this.filter(searchRes =\u003e searchRes.isDiscounted())\n    }\n}\n\n// Initializing is the same as BaseFragment, but you need to pass ElementArrayFinder now.\nlet searchResults = new SearchResultsCollection($$('.search-result'))\n// Awesome readability for your tests\nsearchResults.findResultsWithDiscount().first().open()\n```\n\nBaseArrayFragment also supports additional array methods that does not exist in ElementArrayFinder in ProtractorJS:\n`.every()` `.some()` `.find()`\n\n\nMore tricks\n----------------------\n\nYou can wrap any ElementFinder into your fragment:\n```typescript\nlet checkbox = new Checkbox($$('.checkbox').last())\n```\n--------------------------------\nYou can use your fragments everywhere where ElementFinder is expected. For example - inside `browser.wait`\n```typescript\nbrowser.wait(EC.elementToBeClickable(checkbox), 5000, 'Checkbox should be clickable')\n```\nOr inside `executeScript`:\n```typescript\nlet checkbox = new Checkbox($('div.checkbox'))\nvar tag = browser.executeScript('return arguments[0].tagName', checkbox);\nexpect(tag).toEqual('div');\n```\n--------------------------------\nYou can override ElementFinder or ElementArrayFinder methods, to get even more powerful functionality\n```typescript\n...\nimport {promise} from 'protractor'\n\nclass Checkbox extends BaseFragment {\n  ...\n  // Ovveriding isDisplayed function that is used in ExpectedCondition.visibilityOf()\n  isDisplayed() {\n    let fragmentDisplayed = super.isDisplayed()\n    let loaderDisplayed = $('.loader').isDispayed()\n    // This will return promise, that will be resolved to true, if element is displayed, but loader is not displayed.\n    return promise.all(fragmentDisplayed, loaderDisplayed).then(displArray=\u003e displArray[0] \u0026\u0026 !displArray[1])\n  }\n}\n\n...\nlet checkbox = new Checkbox($('.checkbox'))\nbrowser.wait(EC.visibilityOf(checkbox), 3000, 'Checkbox should be visible, but loader should not be visible')\n```\n--------------------------------\nTry to use fragments by placing fragments one into another:\n```typescript\nclass LoginForm extends BaseFragment {\n    loginField:TextField\n    passwordField:TextField\n    rememberMe:Checkbox\n\n    constructor() {\n        super($('.loginform'))\n        // Notice that we are searching only inside 'this' element, this brings additional stability to tests\n        this.loginField = new TextField(this.$('.loginfield'))\n        this.passwordField = new TextField(this.$('.passwordfield'))\n        this.rememberMe = new Checkbox(this.$('.rememberme'))\n    }\n\n    login(username='test', password='test', rememberme=true) {\n        this.loginField.type(username)\n        this.passwordField.type(password)\n        rememberme \u0026\u0026 this.rememberMe.select()\n        this.$('button.login').click()\n    }\n}\n```\n\nSupported versions\n---------------------\nCurrently tested on \nNodeJS:\n- 6\n- 7\n- 8\n- 9\n- 10\n\nProtractorJS:\n- 5.x\n\nThis lib should work on protractor 4.x without modifications (but this is untested). Protractor 3.x will require `browser_` reference rename. PRs are welcome!\n\nTypings for TypeScript are included.\n\n\nFuture\n----------------------\n\n- Better logging for fragments. Provide possibility to set `name` attribute, and if it is not set - try to generate best we can with `locator()` \n- Want some feature? Feel free to create issue!\n\n\nSomething to read\n----------------------\n\nSource code for ElementFinder and ElementArrayFinder - \n\nhttps://github.com/angular/protractor/blob/master/lib/element.ts\n\nGenerics in TypeScript - \n\nhttps://www.typescriptlang.org/docs/handbook/generics.html\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxotabu4%2Fprotractor-element-extend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxotabu4%2Fprotractor-element-extend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxotabu4%2Fprotractor-element-extend/lists"}