{"id":22311472,"url":"https://github.com/vilicvane/turning","last_synced_at":"2025-07-29T08:32:14.406Z","repository":{"id":42209339,"uuid":"56831536","full_name":"vilicvane/turning","owner":"vilicvane","description":"Automated state transition testing.","archived":false,"fork":false,"pushed_at":"2022-04-10T09:34:54.000Z","size":326,"stargazers_count":10,"open_issues_count":4,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-30T15:42:02.539Z","etag":null,"topics":["e2e-testing","state-transition-testing","testing"],"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/vilicvane.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":"2016-04-22T06:25:41.000Z","updated_at":"2023-02-18T20:50:27.000Z","dependencies_parsed_at":"2022-08-12T09:41:15.020Z","dependency_job_id":null,"html_url":"https://github.com/vilicvane/turning","commit_stats":null,"previous_names":["vilicvane/turning","vilic/turning"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fturning","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fturning/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fturning/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fturning/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vilicvane","download_url":"https://codeload.github.com/vilicvane/turning/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227999357,"owners_count":17853886,"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":["e2e-testing","state-transition-testing","testing"],"created_at":"2024-12-03T21:19:41.294Z","updated_at":"2024-12-03T21:19:41.767Z","avatar_url":"https://github.com/vilicvane.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Turning\n\n\u003e This project is currently just a proof of concept, any feedback is welcome.\n\nAutomated state transition testing.\n\n## Why \u0026 How\n\nWriting E2E test cases is frustrating, and the best we can do in practice usually is just very limited test cases for every single feature.\n\nThe scenario that triggers this idea was testing a package manager similar to `npm`, but it should apply to varieties of scenarios including E2E tests for web apps.\n\nTurning splits the composition of test cases into two parts: **states** and **transitions**.\n\nThe state definitions can verify whether the current context complies the states it's claimed to be; and the transition definitions tell possible paths of how the states transit from one to another in context.\n\n### Example\n\nAssuming we need two states to represent the a web session:\n\n```ts\nturning.define('session:not-logged-in').test(async ({page}) =\u003e {\n  await expect(page).not.toMatchElement('.profile');\n  await expect(page).toMatchElement('.login-link');\n});\n\nturning.define('session:logged-in').test(async ({page}) =\u003e {\n  await expect(page).not.toMatchElement('.login-link');\n  await expect(page).toMatchElement('.profile');\n});\n```\n\nAnd we can transit those two states by two transitions:\n\n```ts\nturning\n  .turn(['session:not-logged-in'])\n  .to(['session:logged-in'])\n  .alias('login')\n  .by(async ({page}) =\u003e {\n    await page.click('.login-link');\n\n    await page.type('input.username', 'admin');\n    await page.type('input.password', '123456');\n\n    await page.click('.login-submit-button');\n\n    await page.waitForNavigation();\n  });\n\nturning\n  .turn(['session:logged-in'])\n  .to(['session:not-logged-in'])\n  .alias('logout')\n  .by(async ({page}) =\u003e {\n    await page.click('.logout-link');\n\n    await page.waitForNavigation();\n  });\n```\n\nThus a test case can be automatically generated:\n\n- `login` -\u003e `logout`\n\nBy introducing more states and transitions, plentiful test cases could be generated without your spending time struggling thinking of different compositions.\n\nTurning currently uses a tricky algorithm to search for available test cases. It tries to find possible combinations of test cases that cover all the states and transitions, while keep the number of test cases reasonable.\n\n## Installation\n\n```sh\nyarn add turning --dev\n```\n\nA test runner (e.g., `jest`) is required.\n\n```sh\nyarn add jest\n```\n\n## Usage\n\n```ts\n// Function `describe` and `test` is defined by your test runner.\nlet turning = new Turning({describe, test});\n\n// Define states:\n\nturning.define('state-a');\n\nturning.define('state-b').test(async context =\u003e {\n  // Assert the context.\n});\n\n// Define initialize nodes:\n\nturning\n  .initialize(['state-a'])\n  .alias('initialize a')\n  .by(async () =\u003e {\n    // Initialize the context to `state-a` and return the context object.\n    return {};\n  });\n\n// Define transition nodes:\n\n// Check out the content below for differences about `turn` and `spawn`.\n\nturning\n  .turn(['state-a'])\n  .to(['state-b'])\n  .alias('a to b')\n  .by(async context =\u003e {\n    // Mutate the context or return a new one.\n  });\n\nturning\n  .spawn(['state-b'])\n  .to(['state-a'])\n  .alias('b to a')\n  .by(async context =\u003e {\n    // Spawn transition must return new context object.\n    return {};\n  });\n\n// Generate test cases with `describe` and `test` provided.\nturning.test().then(passed =\u003e {\n  process.exit(passed ? 0 : 1);\n});\n```\n\nFor now, you can checkout [makeflow-e2e](https://github.com/makeflow/makeflow-e2e) for more usages.\n\n## Transitions\n\nTurning provides two different concepts of transitions: `turn` and `spawn`.\n\n- `turn`: transit a context from states to states.\n- `spawn`: duplicate a context and transit the states.\n\nSo basically if you are using `turn`, every leaf would result in a new test case from the initialization (or recent spawning); and if you are using `spawn`, the spawned branches would begin with the same context before spawn.\n\n## Manual Cases\n\n```ts\nturning.case('manual case 1', ['initialize a', 'a to b', 'b to a', 'a to b']);\n```\n\n## License\n\nMIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fturning","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvilicvane%2Fturning","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fturning/lists"}