{"id":19501250,"url":"https://github.com/acryps/feature","last_synced_at":"2026-07-14T23:31:55.167Z","repository":{"id":244396405,"uuid":"794903083","full_name":"acryps/feature","owner":"acryps","description":null,"archived":false,"fork":false,"pushed_at":"2024-06-14T12:04:11.000Z","size":215,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-26T07:22:17.540Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/acryps.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-05-02T07:30:44.000Z","updated_at":"2024-06-14T11:03:21.000Z","dependencies_parsed_at":"2024-06-14T13:29:05.773Z","dependency_job_id":"606ade42-43b5-4e9c-a58c-93f89d633a57","html_url":"https://github.com/acryps/feature","commit_stats":null,"previous_names":["acryps/feature"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/acryps/feature","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acryps%2Ffeature","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acryps%2Ffeature/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acryps%2Ffeature/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acryps%2Ffeature/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/acryps","download_url":"https://codeload.github.com/acryps/feature/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/acryps%2Ffeature/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35483857,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-14T02:00:06.603Z","response_time":114,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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-11-10T22:12:02.602Z","updated_at":"2026-07-14T23:31:55.152Z","avatar_url":"https://github.com/acryps.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Features - Define your own web application features!\n\n**What is a feature and what can I do with it?**\n\nUsually, your application consists of several different functionalities. \nThis could be, for example, that a user can configure a product on a web configurator or that a user is able to search for a specific product using a search prompt, and so on. \nNow, using Features, you can define those functionalities in code. \nYou can define them step-by-step for your web applications.\n\nUsing those defined features, you can **automatically generate guides, screenshots, and videos!**.\nThus, no more tedious work by hand!\n\n**Why would I want to do that?**\n\nImagine you have finished the first version of your web application and created complete video guides for its functionalities. \nAfter a couple of weeks, the web application is redesigned. Now the videos are outdated, and you have to tediously recreate the video guides again by hand. Not with Features! \nWith Features, you can **simply re-execute the features**, and the video guides are regenerated for the updated web application within seconds.\n\nAdditionally, features can be used to **find bugs and unwanted UI changes across project versions**. \nBy comparing the screenshots of features across different versions, you will see unwanted changes within seconds!\n\n## Requirements\n\nTo use Features, you need to install [ffmpeg](https://ffmpeg.org/).\n\n## Usage\n\nThe following shows an example where a product is configured. \n\nFeatures uses **method chaining** in order **to define the instructions of the feature**. \nEach instruction can simply be chained after the previous one.\n\n```typescript\n// define your project\nconst project = new Project('assembly', 'https://assembly.acryps.com');\n\n// define your own feature\nconst feature = new Feature('basic usage', 'demonstrate basic usage of feature')\n\t// navigate to website\n\t.go(`https://assembly.acryps.com/`)\n\n\t// find html element 'action' which has content 'Action' and click it\n\t.element('action', 'Create').click()\n\n\t// wait while html element 'indicator' is present\n\t.waitWhile('indicator')\n\n\t// find html element 'Body Type' and click it\n\t.element('name', 'Body Type').click()\n\n\t.element('name', 'Wafer').click()\n\n\t.element('action', 'Save As...').click()\n\n\t// find html input element 'save-assembly input' and write 'My Configuration' into input field\n\t.element('save-assembly input').write('My Configuration')\n\t.element('action', 'Add to my Assemblies').click();\n\n// execute the feature for a project\nconst result = await feature.execute(project)\n\t.guide() // generate a guide\n\t.screenshot() // generate screenshots for each step\n\t.video('./basic-usage/video.webm') // generate video\n\t.run();\n\n// save result\nawait result.save('./media/basic-usage');\n```\n\n## Elements\n\nIn order to interact with a web application, we need to be able to tell Features which elements we want to interact with. \nTo do this, we have `element` to handle single elements and `elements` to handle multiple elements. \nBoth of those use 'locators' to search the element on the webpage. \nThese locators describe the HTML tags of the elements.\n\nFor example, if we have the following webpage:\n\n```html\n\u003chtml\u003e\n\t\u003cbody\u003e\n\t\t\u003ctitle\u003e\n\t\t\t\u003cname\u003eTitle\u003c/name\u003e\n\t\t\u003c/title\u003e\n\n\t\t\u003cpanels\u003e\n\t\t\t\u003cpanel\u003e\n\t\t\t\t\u003cname\u003epanel 1\u003c/name\u003e\n\t\t\t\u003c/panel\u003e\n\n\t\t\t\u003cpanel\u003e\n\t\t\t\t\u003cname\u003epanel 2\u003c/name\u003e\n\t\t\t\u003c/panel\u003e\n\t\t\u003c/panels\u003e\n\t\u003c/body\u003e\n\u003c/html\u003e\n```\n\nWe can select the title like this:\n\n```typescript\nfeature.element('title name')\n\t// and then interact with it\n\t.click();\n```\n\nWe can interact with multiple elements, such as the `panels`:\n\n```typescript\n// now we selected all the 'panel' elements\nfeature.elements('panels panel');\n```\n\nTo interact with a single panel, we have to filter them. This can be done in various ways:\n\n```typescript\n// select the first panel\nfeature.elements('panels panel').first()\n\t// then click on the selected element\n\t.click();\n\n// get the element at index 1 (the second element)\nfeature.elements('panels panel').at(1)\n\t// then hover on it\n\t.hover();\n\n// filter according to where conditions\nfeature.elements('panels panel')\n\t// select the panel which has an element 'name' with content 'panel 1'\n\t.where('name', 'panel 1')\n\t.first()\n\t\t.click();\n```\n\nThis is the basic usage of `element` and `elements`. However, the chaining of elements is unlimited, so you can do much more!\n\nPlease refer to the class documentation for detailed descriptions of the functions available for [Feature](./documentation/feature.md), [SingleElement](./documentation/single.md), [MultipleElement](./documentation/multiple.md) and [Select](./documentation/select.md).\n\n## Execution and Result\n\nAfter having defined your own feature, it's time to execute it!\n\nA feature can be executed using `execute`, a `project`, and a `viewport`.\n\n```typescript\nconst project = new Project('example-project', 'https://example.com');\nconst viewport = { width: 1280, height: 720, deviceScaleFactor: 1 };\n\nconst execution = feature.execute(project, viewport);\n```\n\nThis returns an instance of `Execution`. Using an execution, we can specify how we want to execute the feature. \nBy adding `guide`, `screenshot`, or `video`, we can specify if we want to generate a guide, screenshots, or video.\nWe can additionally tell the execution to **not** run in `headless` mode, meaning you will be able to see the browser executing the feature. \nThis can be done by simply adding a boolean value to the run function: `.run(false)`.\n\n```typescript\n// generate guide, screenshot, and video\nconst result = await execution\n\t.guide()\n\t.screenshot()\n\t.video('./media/video.webm')\n\t.run();    // running headless\n```\n\nFinally, you can save your result using the `ExecutionResult`.\n\n```typescript\nawait result.save('./media/example');\n```\n\n## Further Documentation\n\nFor more detailed documentation and extended usage, see [here](./documentation/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facryps%2Ffeature","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Facryps%2Ffeature","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Facryps%2Ffeature/lists"}