{"id":14978860,"url":"https://github.com/testxio/testx","last_synced_at":"2025-06-22T02:37:05.250Z","repository":{"id":21495470,"uuid":"24814383","full_name":"testxio/testx","owner":"testxio","description":"TestX is a keyword-driven functional testing framework","archived":false,"fork":false,"pushed_at":"2022-12-07T17:52:32.000Z","size":3528,"stargazers_count":6,"open_issues_count":6,"forks_count":11,"subscribers_count":8,"default_branch":"3.x","last_synced_at":"2025-05-25T12:41:01.864Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://testx.io","language":"CoffeeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/testxio.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}},"created_at":"2014-10-05T11:29:43.000Z","updated_at":"2023-04-27T09:41:01.000Z","dependencies_parsed_at":"2023-01-14T07:00:30.511Z","dependency_job_id":null,"html_url":"https://github.com/testxio/testx","commit_stats":null,"previous_names":[],"tags_count":70,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testxio%2Ftestx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testxio%2Ftestx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testxio%2Ftestx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testxio%2Ftestx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testxio","download_url":"https://codeload.github.com/testxio/testx/tar.gz/refs/heads/3.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testxio%2Ftestx/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":258222948,"owners_count":22667279,"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":[],"created_at":"2024-09-24T13:58:32.663Z","updated_at":"2025-06-22T02:37:00.236Z","avatar_url":"https://github.com/testxio.png","language":"CoffeeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://badge.fury.io/js/testx.svg)](https://badge.fury.io/js/testx)\n[![Build Status](https://travis-ci.org/testxio/testx.svg?branch=3.x)](https://travis-ci.org/testxio/testx)\n\ntestx\n=====\n\nA library for executing keyword driven tests with Protractor.\n\n- [What is **testx**](#what-is-testx)\n  - [Core principles](#core-principles)\n- [Getting started](#getting-started)\n- [Concepts](#concepts)\n- [API](#api)\n- [Core keywords](#core-keywords)\n\n## What is **testx**\n**testx** is a library for end-to-end keyword driven tests. **testx** IS NOT a framework - you can use it in your [protractor](http://www.protractortest.org) project to make test automation a breeze.\n**testx** is meant for testers. It requires a very limited set of technical skills. It is even suitable for the business people in your project.\n\n### Core principles\n**testx** aims to be **simple**, **extensible** and **easy to integrate**.\n\n#### Simple\nSimplicity is the defining quality of **testx**. It has very few concepts and avoids variations whenever possible. The goal is to have non-technical people feel right at home when automating their tests. This is why **testx** tries to think about everything from the user point of view.\n\n#### Extensible\nIn order to make **testx** as simple as possible we need to make certain sacrifices. The main of those is the fact that **testx** is logic-less. This means that in a **testx** script there are no branches and no loops. A **testx** script is just a list of steps.\n\nHowever, sometimes you need something more complex, like a loop, in your tests. Because of situations like this **testx** makes it very easy to extend it, while keeping the core principle of simplicity intact.    \n\n#### Easy to integrate\n**testx** relies on [protractor](http://protractortest.org) to do the heavy lifting - anything you can do with Protractor is still possible. A very important part is the ability to integrate your test execution into your continuous deployment pipeline. **testx** tries really hard to not make this any more difficult.\n\n## Getting started\n\n### Creating a **testx** project\nA **testx** project is just a [protractor](http://protractortest.org) project. Just install **testx** as a dependency and you can start running your **testx** scripts in (a part of) your test run.\n\n#### The **testx** CLI\nIn line with the core concept of **simplicity**, **testx** provides you with a [CLI](https://www.npmjs.com/package/@testx/cli). It helps you create a **testx** project containing useful examples. Easy!\n\n## Concepts\nThe main thing that **testx** does is to execute (run) a **testx** script. This happens in your protractor specification and looks like this:\n```JavaScript\ndescribe(\"My first\", () =\u003e\n  it(\"testx test!\", () =\u003e\n    testx.run(\"tests/scripts/my-first-test.testx\")));\n```\nAll the rest of it is in the **testx** script file.\n\n### Scripts and steps\nEach **testx** script is just a list of **steps**. These steps are synchronous and are executed in sequence. The script (usually) lives in a *.testx* file. By default this is a YAML file. Every step consists of two things - the keyword to be executed at this step, and the (zero or more) parameters of the execution. In general the steps are just actions or checks performed on objects (elements). Simple!\n\nHere is an example:\n```YAML\n- go to: /\n- set:\n    searchBox: \"testxio\\n\"\n- check matches:\n    orgName: 'testxio'\n```\nThere are 3 **steps** in this script - **go to**, **set** and **check matches**.\n- The **go to** keyword is responsible for taking us to a URL - \"/\" is a URL relative to whatever *baseUrl* (test run parameter) is.\n- The **set** keyword types \"testxio\" in the \"searchBox\" object (it happens to be an input box, we'll talk about objects later) and presses the Enter key (\"\\n\").\n- The **check matches** checks that the content of the \"orgName\" object (an H1 html element in this case) RegEx match the text \"testxio\".\n\n### Keywords\nKeywords tell **testx** what action you want to perform and the parameters of the keyword tell it which element (for example) you want to target. The combination of these two concepts represents a **step**. The type of action can be a click on a button or a link, typing something into an input box, etc. Keywords usually target (do something with) objects - the elements in the browser - or the **test context** (more on that later). The core keywords, the ones that come with **testx**, are generally about actions, that a user performs in the browser. A [list of all core action](#core-keywords) is available further in this documentation.\n\n### Objects\nObjects in **testx** are an abstraction of the HTML elements in the browser. You can think of an object as **the name of a particular, actionable element**. They are discovered by **testx** with a **locator**.\n\nYou need this sort of abstraction to shield you from changes in the exact HTML structure of the application under test. With an object, whenever a \"logical\" element on the screen changes (e.g. you need a different xpath/css to locate the element) changes you'll just change its locator and that's it, you don't need to change any of your scripts.\n\nLocators are organized into what we refer to as **the object map**, but this is just a fancy name for a JSON object, where keys are the name of the object and the values are the locators.\n\nThe names of the objects (in the object map) are how you refer to these objects in your **testx** scripts.\n\n#### Default behaviour\nKey characteristics of **testx** are its simplicity and its ease of use. When it comes down to dealing with objects, this means that **testx** knows what to do with them by default. In other words, when you tell **testx** to **set** a particular object it knows how to do it - it will type into input boxes, select from dropdowns or click buttons.\n\n#### Custom behaviour\nSometimes you want to do something that is not common with a particular object on the screen or maybe you have a complex object that behaves in a very specific way (think of a wysiwyg text editor, for example). In such a case **testx** lets you define the behaviour of this element. This means that when you **set** or **get** (used in checks, for example) this object, custom code will be executed.\n\nTo do this, you need to provide a **behaviour** property to the object. It can have any or all of 3 polymorphic properties - **get**, **set** and **wait**. **Get** is used to retrieve the value of the element and is used by **testx** when it performs checks. **Set** is used by the **set** keyword and specifies the way **testx** manipulates this object. **Wait** describes how **testx** will know if the objects is on the screen and ready for manipulation.\n\nLet's say you have a single link on the page and you want to check if the *href* attribute of that link (and not the default *value* of it, which is the text of the link) equals a certain value. There are a few ways to do that, but an example that uses the custom behaviour would look something like this:\n\nIn your objects definition:\n```JS\nconst objects = {\n  myLink: {\n    locator: \"xpath\",\n    value: \"//a\",\n    behaviour: {\n      get: function() { return this.getAttribute(\"href\") }\n    }\n  }\n}\n```\n\nAnd then in your script:\n```yaml\n- go to: /\n- check equals:\n    myLink: https://testx.io\n```\n\n### Test context\nThe test context includes all *variables* (and functions) you may need during the execution. It allows you to reuse your scripts. For example in this (contrived) script:\n\n```YAML\n- go to: ${customUrl}\n```\n\nyou can pass *customUrl* in the test context and **testx**  will navigate to that URL. This let's you run the same tests against different deployments, for example.\n\n### Parsers\nIt is possible to not use YAML format for your scripts, but instead implement a custom parser for the format you prefer. **TBA**.\n\n### Reporters\nOne of the most important things in every test execution is reporting the results. There are 3 types of default reporters, but you can implement your own (**TBA**).\n\nThe default ones are **console**, **junit** and **HTML**. They are found in the [testx-jasmine-reporters](https://github.com/testxio/testx-jasmine-reporters) project - check it out to see how to use the reporters.\n\n## API\n### Run a script\nThe **testx** API is pretty simple. The only thing you absolutely need to know is how to run a **testx** script. Once you have *require*-ed **testx** (you have to do this in the *onPrepare* function of your Protractor configuration) you can run a **testx** script in you specs like:\n\n```JS\nconst context = {\n  someVar: 1,\n  anotherVar: \"text\",\n  someFunc: () =\u003e \"more text\"\n}\n\ntestx.run(\"path/to/the/testx-script.yml\");\ntestx.run(\"path/to/the/testx-script.yml\", context);\n```\n\n### Add objects to the test execution\n```JS\ntestx.objects.add(myObjectsDict);\n```\n\nUsually you'll have the objects defined somewhere else and a lot more common is:\n```JS\ntestx.objects.add(require(\"path/to/myObjects.js\"));\n```\n\nIt is possible to use a CSV file to define your objects. In this case you'd do:\n```JS\ntestx.objects.add(\"path/to/myObjects.csv\");\n```\n\n### Add stuff to the test context\nAdding variables and functions to the context is also very simple:\n```JS\ntestx.context.add({\"myVar\": \"some text\"});\n```\n\n### Add custom keywords to the execution\nIt is of course possible to add custom keywords to the execution. This is very common for non-trivial tests.\n\nYou'll add keywords like so:\n```JS\nconst keywords = {\n  myKwd: (kwdArguments, context) =\u003e //do something here\n}\n\ntestx.keywords.add(keywords);\n```\n\nAnd then use them like so:\n```yaml\n- myKwd:\n    firstArg: 1\n    secondArg: this is another argument\n```\n\n## Core keywords\nThese are the default keywords that come with **testx**.\n\n### Set\n**Set** performs an action on an element. It is polymorphic, meaning that the action depends on the type of the element. For example a **set** on an *input* will fill it in, while a **set** on a *button* will click it.\n```YAML\n- set:\n    myInput: this text goes in\n    myButton: null\n```\n\nIn this example *myInput* is the reference (the name) of an *input* element and \"this text goes in\" will be \"typed\" in the input element. On the other hand *myButton* is a button that will be clicked. Actions on the elements are ordered, in this example the text will be typed in the input box firs and only then the button gets clicked.\n\nPassing **null** as the action means **click**. So\n```YAML\n- set:\n    myInput: null\n```\nwill cause **testx** to only click in the input box *myInput*.\n\nDefault actions per element type are: **TBA**\n\n### Check\nCheck text, attribute value, existence, enabled or readonly properties of an object.\n```YAML\n- check equals:\n    searchBox: testxio\n    resLink('testxio'): testxio · GitHub\n- check equals:\n    resLink('testxio'):\n      href: https://github.com/testxio\n      host: github.com\n      isConnected: 'true'\n      nodeType: '1'\n- check matches:\n    resLink('testxio'):\n      href: github\\.com/\\w{4}xio\n      localName: \\w\n- check matches:\n    resLink('testxio'):\n      href: https://github\\..om/testxio\n      host: github\\.com\n      isConnected: 't..e'\n      nodeType: '[1|2]'\n- check matches:\n    searchBox: test[x|z].o\n    resLink('testxio'): tx\\w{2} · GitHub\n- check not equals:\n    searchBox: testx1111.io\n    resLink('testxio'): something else\n- check not matches:\n    searchBox: testx1111\\.io\n    resLink('testxio'): test11o\n- check exists:\n    resLink('testxio'): true\n    resLink('no such thing'): false\n- check enabled:\n    resLink('testxio'): true\n    searchBox: true\n- check readonly:\n    resLink('testxio'): false\n    searchBox: false\n```\n\n### Wait\nWait for the (dis)appearance of an object.\n```YAML\n- wait: googleSearchForm\n- wait to appear: googleSearchForm\n- wait:\n    - resLink('{{match}}')\n    - googleSearchForm\n- wait:\n    to: appear\n    objects:\n      - resLink('{{match}}')\n- wait to appear:\n    - resLink('{{match}}')\n- wait to appear:\n    timeout: 2s\n    objects:\n      - resLink('{{match}}')\n- wait:\n    to: disappear\n    objects:\n      - resLink('no such thing')\n- wait to disappear: resLink('no such thing, really')\n- wait to disappear:\n    - resLink('no such thing, really')\n```\n\n### Navigational keywords\n```YAML\n- go to:\n    url: / # go to a path relative to the baseUrl\n- go to: /test/index.html # shortcut to the version above\n- go to: http://testx.io\n- go back\n- go forward\n- refresh page\n```\n\n### Expect\nAssertions. **expect result** checks the result of the keyword executed before it. It can be used as a keyword, but it can be passed as a parameter to any other keyword as well. In the example below the **id** is a custom keyword that just returns its parameters, i.e. they are the result of the keyword.\n\nThese are useful when you want to do some processing of a text, that you get from the screen, and only then do assert. Use the **check** keywords when wanting to directly assert values of objects.\n```YAML\n- go to:\n    url: /\n    expect result:\n      to equal: /\n      not to equal: f\n      to match: .\n      not to match: d\n- custom kwd that just returns params:\n    test: test123\n    expect result:\n      to equal:\n        test: test123\n      not to equal:\n        test: sdaf\n- expect:\n    ${$result.test}:\n      to equal: test123\n      not to equal: something else\n      to match: \\w{4}\\d{3}\n      not to match: completely different\n- put:\n    someVar: 1\n- expect:\n    ${someVar}:\n      to equal: 1\n- id:\n    test: test123\n- expect result:\n    to equal:\n      test: test123\n```\n\n### Run\nRuns another **testx** script, optionally passing it a context:\n```YAML\n- run:\n    script: tests/scripts/sample.testx # the script to run\n    context: # this goes in the testx context\n      myVar: myVal\n- run: tests/scripts/no-context.testx # no context shortcut\n```\n\n### Context manipulation keywords\n#### Put\nPuts stuff in the test context of **testx**.\nThe following example puts 2 values in the test context - an object bound to the *myFirstVar* variable and an array bound to the *secondVar* variable. These values can be used in subsequent steps with `${myFirstVar}` and `${secondVar}`\n```YAML\n- put:\n    myFirstVar:\n      one: two\n      three:\n        - four\n        - and five more\n      six: 'seven nine,ten'\n    secondVar:\n      - nine\n      - ten\n```\n#### Save\nSaves the current value of an object into a context variable. In the example below the value (say text) of *myElement* is saved to *myContextVar*; you'll be able to retrieve it in subsequent steps with `${myContextVar}`\n```YAML\n- save #\n    myContextVar: myElement\n```\n\n### Browser keywords\n```YAML\n- clear local storage\n- delete cookies\n- ignore synchronization: true\n- ignore synchronization: yes\n- ignore synchronization: false\n```\n\n#### Switch to a different window or frame\nThis keyword will switch the test execution to different window or frame.\n```YAML\n- switch to:\n    title: \"My new window\"\n- switch to:\n    frame: \"My new frame ID\"\n```\n\n#### Respond to a dialog\n```YAML\n- respond to dialog: OK\n- respond to dialog:\n    response: ok\n- respond to dialog:\n    response: Cancel\n```\n\n### Debugging keywords\nThe **sleep** keyword will pause the execution of the test. It is strongly recommended that you only use it only while developing your tests.\n```YAML\n- sleep: 5s # the execution will stop here for 5 seconds\n- sleep: 500ms # the execution will stop here for half a second\n```\n\n## Run configuration\nTBA\n\n## Plugins\nA core characteristic of **testx** is its extensibility. This means it is very easy to develop keywords packages, for example. It is of course possible to extend **testx** in other areas of functionality, but 99% of the time you'll extend with a keyword package.\n\nHere is what we have so far:\n| Npm package | Description |\n| ----------- | ----------- |\n|[testx-http-keywords](https://www.npmjs.com/package/testx-http-keywords)|Keywords to send simple http requests using the testx library.|\n|[testx-file-keywords](https://www.npmjs.com/package/testx-file-keywords)|Keywords to test file (text and pdf) content using the testx library.|\n|[testx-pop3-keywords](https://www.npmjs.com/package/testx-pop3-keywords)|Keywords to test mailserver using pop3.|\n|[testx-postgres-keywords](https://www.npmjs.com/package/testx-postgres-keywords)|Keywords to test postgres db.|\n|[testx-soap-keywords](https://www.npmjs.com/package/testx-soap-keywords)|Extension for testx to test soap services.|\n\n## Editor integration\nTo make developing tests easier we've developed an [Atom syntax plugin](https://atom.io/packages/language-testx).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestxio%2Ftestx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftestxio%2Ftestx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestxio%2Ftestx/lists"}