{"id":19206768,"url":"https://github.com/hausgold/factory_bot_instrumentation","last_synced_at":"2025-05-12T17:28:01.805Z","repository":{"id":45495631,"uuid":"164857156","full_name":"hausgold/factory_bot_instrumentation","owner":"hausgold","description":"Allow your testers to generate test data on demand","archived":false,"fork":false,"pushed_at":"2025-05-12T08:53:53.000Z","size":404,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-05-12T10:03:41.991Z","etag":null,"topics":["api","factory-bot","gem","oss","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/factory_bot_instrumentation","language":"Ruby","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/hausgold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null}},"created_at":"2019-01-09T12:17:08.000Z","updated_at":"2025-05-12T08:53:56.000Z","dependencies_parsed_at":"2025-02-25T10:22:31.030Z","dependency_job_id":"addf1c4d-4fb9-4ca6-82d5-b669d83d1802","html_url":"https://github.com/hausgold/factory_bot_instrumentation","commit_stats":{"total_commits":36,"total_committers":4,"mean_commits":9.0,"dds":"0.11111111111111116","last_synced_commit":"b37f2788549440b4176e2e85fdcdea72492aa2f6"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Ffactory_bot_instrumentation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Ffactory_bot_instrumentation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Ffactory_bot_instrumentation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hausgold%2Ffactory_bot_instrumentation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hausgold","download_url":"https://codeload.github.com/hausgold/factory_bot_instrumentation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253717586,"owners_count":21952514,"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":["api","factory-bot","gem","oss","ruby"],"created_at":"2024-11-09T13:16:55.781Z","updated_at":"2025-05-12T17:28:01.724Z","avatar_url":"https://github.com/hausgold.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"![factory_bot_instrumentation](doc/assets/project.svg)\n\n[![Continuous Integration](https://github.com/hausgold/factory_bot_instrumentation/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/hausgold/factory_bot_instrumentation/actions/workflows/test.yml)\n[![Gem Version](https://badge.fury.io/rb/factory_bot_instrumentation.svg)](https://badge.fury.io/rb/factory_bot_instrumentation)\n[![Test Coverage](https://automate-api.hausgold.de/v1/coverage_reports/factory_bot_instrumentation/coverage.svg)](https://knowledge.hausgold.de/coverage)\n[![Test Ratio](https://automate-api.hausgold.de/v1/coverage_reports/factory_bot_instrumentation/ratio.svg)](https://knowledge.hausgold.de/coverage)\n[![API docs](https://automate-api.hausgold.de/v1/coverage_reports/factory_bot_instrumentation/documentation.svg)](https://www.rubydoc.info/gems/factory_bot_instrumentation)\n\nThis project is dedicated to provide an API and frontend to\n[factory_bot](https://github.com/thoughtbot/factory_bot) factories to generate\ntest data on demand. With the help of this gem your testers are able to\ninteract easily with the entities of your application by using predefined use\ncases.\n\n- [Installation](#installation)\n- [Getting started](#getting-started)\n- [Usage](#usage)\n  - [Configuration](#configuration)\n    - [Instrumentation](#instrumentation)\n    - [Routes](#routes)\n    - [Authentication](#authentication)\n    - [Global settings](#global-settings)\n  - [API](#api)\n    - [Request](#request)\n    - [CORS](#cors)\n    - [Response](#response)\n    - [Hot reloading](#hot-reloading)\n  - [Frontend](#frontend)\n    - [Custom hooks](#custom-hooks)\n      - [preCreate](#precreate)\n      - [postCreate](#postcreate)\n      - [preCreateResult](#precreateresult)\n      - [postCreateResult](#postcreateresult)\n      - [preCreateError](#precreateerror)\n      - [postCreateError](#postcreateerror)\n    - [Navigation](#navigation)\n    - [Additional blocks](#additional-blocks)\n    - [Custom scripts](#custom-scripts)\n    - [Custom styles](#custom-styles)\n- [Development](#development)\n- [Code of Conduct](#code-of-conduct)\n- [Contributing](#contributing)\n- [Releasing](#releasing)\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'factory_bot_instrumentation'\n```\n\nAnd then execute:\n\n```bash\n$ bundle\n```\n\nOr install it yourself as:\n\n```bash\n$ gem install factory_bot_instrumentation\n```\n\n## Getting started\n\nSay you have a complex application with lots of factories which are\ninterconnected and you want to test some fixed scenarios from another\napplication test suite (eg. end-to-end testing). Then you need to prepare some\nseeds on each involved application before the test suite starts. Now your test\nsuite deletes some entities inside a regular test to verify the frontend\napplication is working as expected. Whoop. A failure happens on the frontend\nand a lot of tests fail due to the root cause that an entity was not\ndeleted/recreated.\n\nA better solution whould be a dynamic seed generation per test case. So an\nentity may be deleted in an isolated manner, due to a random seed. Thats even\nbetter than looking for a statically seeded entity on a list for example,\nbecause you can now just focus on the single entry which was dynamically\ngenerated for your insolated test case.\n\nAnother use case for dynamic seeds are explorative testers. They could benefit\nfrom the entities your factories are already generating. But on canary, or\nproduction like environments they are not able to access an Rails console to\ntrigger the factory_bot factories. Thats where `factory_bot_instrumentation`\ncomes in.\n\n## Usage\n\nLets start with a common factory_bot factory. Say your application handles user\naccounts and a single user may have multiple friends with a self reference.\n(the association does not matter) Than the factory could look like this:\n\n```ruby\nFactoryBot.define do\n  factory :user do\n    first_name { FFaker::NameDE.first_name }\n    last_name { FFaker::NameDE.last_name }\n\n    transient do\n      friend_traits { [] }\n      friend_overwrites { {} }\n      friends_amount { 2 }\n    end\n\n    trait :confirmed do\n      after(:create, \u0026:confirm!)\n    end\n\n    trait :with_friend do\n      after(:create) do |user, elevator|\n        FactoryBot.create(:user,\n                          *elevator.friend_traits.map(\u0026:to_sym),\n                          friends: [user],\n                          **elevator.friend_overwrites)\n      end\n    end\n\n    trait :with_friends do\n      after(:create) do |user, elevator|\n        FactoryBot.create_list(:user,\n                               elevator.friends_amount,\n                               *elevator.friend_traits.map(\u0026:to_sym),\n                               friends: [user],\n                               **elevator.friend_overwrites)\n      end\n    end\n  end\nend\n```\n\nAt your specs you would use it somehow like this:\n\n```ruby\nlet(:user) { create :user }\nlet(:user_with_single_friend) { create :user, :with_friend }\nlet(:user_with_single_friend_bob) do\n  create :user,\n         :with_friend,\n         friend_overwrites: { first_name: 'Bob' }\nend\nlet(:user_with_many_friends) { create :user, :with_friends, friends_amount: 5 }\n```\n\nWith the Instrumentation engine you allow external users to trigger your\nfactories the same way via an API (HTTP request) or with preconfigured\nscenarios via an easy to use frontend. Thats it.\n\n### Configuration\n\n#### Instrumentation\n\nThe Instrumentation engine works with preconfigured scenarios on the frontend\nas well as adhoc requests via the API. By default it requires a\n`config/instrumentation.yml` inside your application where all scenarios are\ndefined. You can use the following example as a starting point to your\nconfiguration.\n\n```yaml\n# Define new dynamic seed scenarios here which can be used on the API\n# instrumentation frontend.\n\ndefault: \u0026default\n  # Each group consists of a key (the pattern to match) and the value (group\n  # name).  The patterns are put inside a quoted regex and the first matching\n  # one will be used so the configuration order is important.\n  groups:\n    UX: UX Scenarios\n    user: Users\n\n  # All the scenarios which can be generated.\n  scenarios:\n    - name: Empty user\n      desc: Create a new user without any dependent data.\n      factory: :user\n      traits:\n        - :confirmed\n      overwrite: {}\n\n    - name: User with a single friend\n      desc: Create a new user with a single friend.\n      factory: :user\n      traits:\n        - :confirmed\n        - :with_friend\n      overwrite: {}\n\n    - name: User with a single friend named Bob\n      desc: Create a new user with a single friend whoes name is Bob.\n      factory: :user\n      traits:\n        - :confirmed\n        - :with_friend\n      overwrite:\n        friend_overwrites:\n          first_name: Bob\n\n    - name: User with multiple friends\n      desc: Create a new user with 5 friends.\n      factory: :user\n      traits:\n        - :confirmed\n        - :with_friends\n      overwrite:\n        friends_amount: 5\n\ntest:\n  \u003c\u003c: *default\n\ndevelopment:\n  \u003c\u003c: *default\n\nproduction:\n  \u003c\u003c: *default\n```\n\n#### Routes\n\nYou can mount the Instrumentation engine (API and frontend) easily into your\nRails application the following way:\n\n```ruby\nRails.application.routes.draw do\n  mount FactoryBot::Instrumentation::Engine =\u003e '/instrumentation'\nend\n```\n\nIn cases you want to enhance the functionality under the same namespace, you\ncould mount the Instrumentation engine like this:\n\n```ruby\nRails.application.routes.draw do\n  namespace :instrumentation do\n    mount FactoryBot::Instrumentation::Engine =\u003e '/'\n    resource :authentication, only: :create\n  end\nend\n```\n\nThe `Instrumentation::Authentication` controller must be implemented by your\napplication. The file\n`app/controllers/instrumentation/authentications_controller.rb` could look like\nthis:\n\n```ruby\nclass Instrumentation::AuthenticationsController \\\n      \u003c FactoryBot::Instrumentation::ApplicationController\n  # Generate a new web app authentication URL for the given email address.\n  # This endpoint creates new login URLs which are valid for 30 minutes.\n  def create\n    render json: { url: url }\n  end\n\n  private\n\n  def url\n    User.find_by(email: params.permit(:email).fetch(:email)).auth_url\n  end\nend\n```\n\n#### Authentication\n\nBy default the Instrumentation engine comes without authentication at all to\nease the integration. But as you can imagine the Instrumentation engine opens\nup some risky possibilities to your application. This is fine for a canary or\ndevelopment environment, but not for a production environment.\n\nThere are currently multiple ways to secure the Instrumentation engine. You can\ncompletely disable it on your production environment by reconfiguring your\nroutes like this:\n\n```ruby\nRails.application.routes.draw do\n  if ActiveModel::Type::Boolean.new.cast(ENV.fetch('INSTRUMENTATION_API',\n                                                   false))\n    mount FactoryBot::Instrumentation::Engine =\u003e '/instrumentation'\n  end\n\n  # or with custom controllers in an namespace\n\n  if ActiveModel::Type::Boolean.new.cast(ENV.fetch('INSTRUMENTATION_API',\n                                                   false))\n    namespace :instrumentation do\n      mount FactoryBot::Instrumentation::Engine =\u003e '/'\n      resource :authentication, only: :create\n    end\n  end\nend\n```\n\nAnother option would be an HTTP basic authentication. Just configure the gem\nlike this on the initializer:\n\n```ruby\nFactoryBot::Instrumentation.configure do |conf|\n  conf.before_action = proc do |controller|\n    basic_auth(username: ENV.fetch('INSTRUMENTATION_USERNAME'),\n               password: ENV.fetch('INSTRUMENTATION_PASSWORD'))\n  end\nend\n```\n\n#### Global settings\n\nBeside the configurations from above comes some gem settings you can tweak. The\nbest place for this would be an initializer at your Rails application. (eg.\n`config/initializers/factory_bot_instrumentation.rb`) Here comes an example\nwith inline documentation of the settings.\n\n```ruby\nFactoryBot::Instrumentation.configure do |conf|\n  # You can set a fixed application name here,\n  # defaults to your Rails application name in a titlized version\n  conf.application_name = 'User API'\n  # The instrumentation configuration file path we should use,\n  # defaults to config/instrumentation.yml\n  conf.config_file = 'config/scenarios.yml'\n  # By default we use the Rails default JSON rendering mechanism, but\n  # you can configure your own logic here (eg. a custom representer)\n  conf.render_entity = proc do |controller, entity|\n    controller.render plain: entity.to_json,\n                      content_type: 'application/json'\n  end\n  # By default we assemble a JSON response on errors which may be\n  # helpful for debugging, but you can configure your own logic here\n  conf.render_error = proc do |controller, error|\n    app_name = FactoryBot::Instrumentation.configuration.application_name\n    controller.render status: :internal_server_error,\n                      content_type: 'application/json',\n                      plain: {\n                        application: app_name,\n                        error: error.message,\n                        backtrace: error.backtrace.join(\"\\n\")\n                      }.to_json\n  end\n  # By default we do not perform any custom +before_action+ filters on the\n  # instrumentation controllers, with this option you can implement your\n  # custom logic like authentication\n  conf.before_action = proc do |controller|\n    # do your custom logic here\n  end\nend\n```\n\n### API\n\nThe Instrumentation engine comes with a single API endpoint which allows you to\ntrigger your factory_bot factories with traits and overwrites. Thats just as\nsimple as it sounds.\n\nThe endpoint is at the same path as you mounted the engine. Say you mounted the\nengine at `/instrumentation`, then you can send an `POST` request to this path.\n\n#### Request\n\nA sample request body looks like this:\n\n```json\n{\n  \"factory\": \"user\",\n  \"traits\": [\"confirmed\"],\n  \"overwrite\": {\n    \"first_name\": \"Bernd\",\n    \"last_name\": \"Müller\",\n    \"email\": \"bernd.mueller@example.com\",\n    \"password\": \"secret\"\n  }\n}\n```\n\nWhen sending requests to this endpoint make sure to send the correct\naccept/content-type headers. (`Accept: application/json`, `Content-Type:\napplication/json`)\n\n#### CORS\n\nIn case you want to deal with this endpoint from a different frontend\napplication via XHR calls (AJAX) you need to set the CORS headers for your\napplication. See [rack-cors](https://github.com/cyu/rack-cors) - and the\nfollowing naive example:\n\n```ruby\nRails.application.config.middleware.insert_before 0, Rack::Cors do\n  allow do\n    origins '*'\n    resource '*',\n             headers: :any,\n             methods: %i[get post put patch delete options head]\n  end\nend\n```\n\n#### Response\n\nThe response of this endpoint is always the generated entity as JSON\nrepresentation. (Just like this: `FactoryBot.create(:user).to_json`)\n\n#### Hot reloading\n\nAs you configure your scenarios and enhance your factory_bot factories you\ndon't have to reboot your application to get the new configuration read or the\nfactory code reloaded manually. Each time you reload the Instrumentation\nfrontend on your browser, the configuration file is reread. The same is true\nfor API requests - the factories are reloaded before each request.\n\n### Frontend\n\n* [Regular Instrumentation Frontend](doc/assets/regular.png)\n* [Fully customized Instrumentation Frontend](doc/assets/customized.png)\n\n#### Custom hooks\n\nYou can define some custom hooks to enhance the functionality. With the help\nof the following hooks you are able to customize the outputs, perform\nadditional HTTP requests or anything you like.\n\nAll the hooks are designed to passthrough a payload. They receive this\npayload as the first argument, and a callback function to signal the end of\nthe hook. You MUST pass the payload as second parameter to the callback, or\npass an error object as first argument. You can modify the payload as you\nwish, eg. adding some data from subsequent requests.\n\nExample hooks:\n\n```javascript\n// Error case\nwindow.hooks.postCreate.push((payload, cb) =\u003e {\n  cb({ error: true});\n});\n\n// Happy case\nwindow.hooks.postCreate.push((payload, cb) =\u003e {\n  cb(null, Object.assign(payload, { additional: { data: true } }));\n});\n```\n\nMind the fact that you can define multiple custom functions per hook type.\nThey are executed after each other in a waterfall like flow. The order of\nthe hooks array is therefore essential.\n\nThe best place to put your custom hooks is the `_scripts.html.erb` partial. See\nthe [Custom scripts](#custom-scripts) section below. Here comes a sample:\n\n```html\n\u003cscript\u003e\n  window.hooks.postCreate.push((payload, cb) =\u003e {\n    // Perform your request\n    window.utils.request({\n      data: JSON.stringify({ email: payload.email }),\n      url: '\u003c%= main_app.instrumentation_authentication_path %\u003e',\n    }, (err, result) =\u003e {\n      if (err) { return cb(err); }\n      if (!result.url) { return cb(data); }\n      cb(null, Object.assign(payload, { auth: result }));\n    });\n  });\n\n  window.hooks.preCreateResult.push((options, cb) =\u003e {\n    // Append some text to the alert message\n    options.alert += ` Some additional alert content.`;\n    // Or just overwrite the whole alert content\n    options.alert = 'Special alert!';\n    // Create a custom card and add it to the accordion\n    options.cards.push(window.utils.card({\n      id: 'auth',\n      icon: 'fa-key',\n      title: 'Authentication',\n      body: `\n        \u003cpre id=\"auth-data\"\u003e${options.payload.auth}\u003c/pre\u003e\n        ${window.utils.clipboardButton('auth-data')}\n      `\n    }));\n    // Don't forget to call the callback function\n    cb(null, options);\n  });\n\u003c/script\u003e\n```\n\nTo access your named application routes, you have to use the `main_app` helper.\nSo a regular `\u003c%= root_path %\u003e` becomes `\u003c%= main_app.root_path %\u003e` while\nadding some custom scripts/styles/blocks.\n\n##### preCreate\n\nWith the help of the `perCreate` hooks you can manipulate the create\nrequest parameters. Think of an additional handling which reads an\noverwrite form or a kind of trait checkboxes to customize the factory\ncall. The `payload` looks like this:\n\n```javascript\n{\n  factory: 'user',\n  traits: ['confirmed'],\n  overwrite: { password: 'secret' }\n}\n```\n\n##### postCreate\n\nThe `postCreate` hook allows you to perform subsequent requests to fetch\nadditional data. Think of a user instrumentation where you want to request\na one time token for this user. This token can be added to the payload and\ncan be shown with the help of the `preCreateResult` hook. The payload\ncontains the request parameters and the response body from the\ninstrumentation request. Here comes an example `payload`:\n\n```javascript\n{\n  request: { factory: 'user', /* [..] */ },\n  response: { /* [..] */ }\n}\n```\n\n##### preCreateResult\n\nWith the help of the `preCreateResult` hook you can customize the output\nof the result. You could also perform some subsequent requests or some UI\npreparations. You can access the output options and the runtime payload\nwith all its data and make modifications to them. This hook is triggered\nbefore the result is rendered. A sample payload comes here:\n\n```javascript\n{\n  alert: 'Your alert text.',\n  output: 'Formatted response',\n  payload: { request: { /* [..] */ }, response: { /* [..] */ } },\n  cards: [\n    `The details accordion card,\n     you can add more, remove the details card\n     or reorder them`\n  ],\n  openCard: '#details', // Open a custom card, or none\n  pre: 'Additinal HTML content before the alert.',\n  post: 'Additinal HTML content after the formatted response output.'\n}\n```\n\n##### postCreateResult\n\nIn case you want to perform some logic after the result is rendered, you\ncan use the `postCreateResult` hook. You can access the output options and\nthe runtime payload with all its data, but changes to them won't take\neffect. The `payload` looks like this:\n\n```javascript\n{\n  alert: 'Your alert text.',\n  output: 'Formatted response',\n  payload: { request: { /* [..] */ }, response: { /* [..] */ } },\n  cards: [\n    `The details accordion card,\n     you can add more, remove the details card\n     or reorder them`\n  ],\n  openCard: '#details', // Open a custom card, or none\n  pre: 'Additinal HTML content before the alert.',\n  post: 'Additinal HTML content after the formatted response output.'\n}\n```\n\n##### preCreateError\n\nWith the help of the `preCreateError` hook you can customize the output of\nthe error. Furthermore you can perform some subsequent requests or\nwhatever comes to your mind. You can access the output options and the\nruntime payload with all its data and make modifications to them. This\nhook is triggered before the error is rendered. A sample payload comes\nhere:\n\n```javascript\n{\n  alert: 'Your alert text.',\n  output: 'Formatted response',\n  payload: { request: { /* [..] */ }, response: { /* [..] */ } },\n  pre: 'Additinal HTML content before the alert.',\n  post: 'Additinal HTML content after the formatted response output.'\n}\n```\n\n##### postCreateError\n\nIn case you want to perform some magic after an error occured, you can use\nthe `postCreateError` hook. You can access the output options and the\nruntime payload with all its data, but changes to them won't take effect\nbecause this hook is triggered after the error is rendered. The `payload`\nlooks like this:\n\n```javascript\n{\n  alert: 'Your alert text.',\n  output: 'Formatted response',\n  payload: { request: { /* [..] */ }, response: { /* [..] */ } },\n  pre: 'Additinal HTML content before the alert.',\n  post: 'Additinal HTML content after the formatted response output.'\n}\n```\n\n#### Navigation\n\n![Customized navigation](doc/assets/navigation.png)\n\nYou can customize the navigation of the Instrumentation frontend by creating\nthe `app/views/factory_bot_instrumentation/_navigation.html.erb` inside your\napplication. This could be useful to create additional links to documentations\n(or maybe an inline documentation page), some custom instrumentation actions,\netc. Here comes a sample `_navigation.html.erb`:\n\n```html\n\u003cli class=\"nav-item active\"\u003e\n  \u003ca class=\"nav-link\" href=\"#\"\u003eHome\u003c/span\u003e\u003c/a\u003e\n\u003c/li\u003e\n\u003cli class=\"nav-item\"\u003e\n  \u003ca class=\"nav-link\" href=\"#\"\u003eLink\u003c/a\u003e\n\u003c/li\u003e\n```\n\n#### Additional blocks\n\n![Customized blocks](doc/assets/blocks.png)\n\nIn some cases you want to add additional functionality to the Instrumentation\nfrontend like the feature to login random users, or trigger special behaviour\nof your application. This is done by custom blocks which provide an easy way to\nenhance the frontend. Just create a new\n`app/views/factory_bot_instrumentation/_blocks.html.erb` inside your\napplication. In case you have multiple custom blocks it comes in handy to split\neach block into its own partial. Therefore you could create a subdirectory like\n`app/views/factory_bot_instrumentation/blocks/` and place a `_example.html.erb`\nfile into it. The `_blocks.html.erb` can than include the partials this way:\n\n```html\n\u003c%= render partial: 'factory_bot_instrumentation/blocks/example' %\u003e\n```\n\nAn example block could look like this:\n\n```html\n\u003cform id=\"authenticate-email\" class=\"jumbotron\"\u003e\n  \u003cdiv class=\"form-group\"\u003e\n    \u003cinput type=\"email\" class=\"form-control email\"\n           placeholder=\"Enter email\"\u003e\n    \u003csmall id=\"help\" class=\"form-text text-muted\"\u003e\n      This will generate a new direct authentication link for the\n      specified user. You do not need to know the actual password to\n      test the user account.\n    \u003c/small\u003e\n  \u003c/div\u003e\n  \u003cbutton id=\"login\" type=\"submit\" class=\"btn btn-primary btn-block\"\u003e\n    Login by email\n  \u003c/button\u003e\n\u003c/form\u003e\n\u003cscript\u003e\n  $(() =\u003e {\n    const scope = '#authenticate-email';\n    const form = new Form(scope);\n\n    form.email = $(`${scope} .email`);\n\n    form.bind((event) =\u003e {\n      async.waterfall([\n        Utils.pushWaterfallPayload({ email: form.email.val() }),\n        (payload, cb) =\u003e {\n          // Perform your request (See: utils for a request helper)\n        }\n      ], (err, result) =\u003e {\n        if (err) { return form.showError(err, err.responseText || err); }\n        form.showResult(result, result.response);\n      });\n    });\n\n    form.errorContent = function(payload, output, cb)\n    {\n      cb(null, `\n        \u003cdiv class=\"alert alert-danger\" role=\"alert\"\u003e\n          An unexpected error occured.\n        \u003c/div\u003e\n        \u003cpre id=\"data\"\u003e${output}\u003c/pre\u003e\n        ${window.utils.clipboardButton('data')}\n      `);\n    };\n\n    form.resultContent = function(payload, output, cb)\n    {\n      let alertPayload = {\n        email: payload.email,\n        url: payload.response.url\n      };\n      cb(null, `\n        \u003cdiv class=\"alert alert-success\" role=\"alert\"\u003e\n          \u003ca href=\"${payload.response.url}\"\u003eLogin now\u003c/a\u003e to a\n          ${payload.email} session.\n        \u003c/div\u003e\n        \u003cpre id=\"data\"\u003e${output}\u003c/pre\u003e\n        ${window.utils.clipboardButton('data')}\n      `);\n    };\n  });\n\u003c/script\u003e\n```\n\n#### Custom scripts\n\nYou can also include some custom scripts which could load additional libraries,\nor add some custom library code. Just create a\n`app/views/factory_bot_instrumentation/_scripts.html.erb` inside your\napplication. And fill in your content. Example file:\n\n```html\n\u003cscript\u003e\n  window.lib = Lib = {};\n  Lib.alert = () =\u003e alert('This is a test.');\n\u003c/script\u003e\n```\n\nSee\n[utils.js](app/assets/javascripts/factory_bot_instrumentation/lib/utils.js),\nand [form.js](app/assets/javascripts/factory_bot_instrumentation/lib/form.js)\nfor some helpers you can use at your custom hooks or custom scripts.\n\n#### Custom styles\n\nNext to scripts you can place some custom styles. This can be very helpful for\ncustom functionality like blocks or complete custom Instrumentation pages. Just\ncreate `app/views/factory_bot_instrumentation/_styles.html.erb` inside your\napplication. The file could look like this:\n\n```hmtl\n\u003clink rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/dark.min.css\"\n      integrity=\"sha256-GVo4WKmO61/tVmRyEKLvRm2Nnq7mdFCaOim/9HbNpaM=\"\n      crossorigin=\"anonymous\" /\u003e\n\u003cstyle\u003e\n  pre {\n    margin-bottom: 0;\n    white-space: pre-wrap;\n    white-space: -moz-pre-wrap;\n    white-space: -pre-wrap;\n    white-space: -o-pre-wrap;\n    word-wrap: break-word;\n  }\n\u003c/style\u003e\n```\n\n## Development\n\nAfter checking out the repo, run `make install` to install dependencies. Then,\nrun `make test` to run the tests. You can also run `make shell-irb` for an\ninteractive prompt that will allow you to experiment.\n\n## Code of Conduct\n\nEveryone interacting in the project codebase, issue tracker, chat\nrooms and mailing lists is expected to follow the [code of\nconduct](./CODE_OF_CONDUCT.md).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/hausgold/factory_bot_instrumentation. Make sure that every pull request adds\na bullet point to the [changelog](./CHANGELOG.md) file with a reference to the\nactual pull request.\n\n## Releasing\n\nThe release process of this Gem is fully automated. You just need to open the\nGithub Actions [Release\nWorkflow](https://github.com/hausgold/factory_bot_instrumentation/actions/workflows/release.yml)\nand trigger a new run via the **Run workflow** button. Insert the new version\nnumber (check the [changelog](./CHANGELOG.md) first for the latest release) and\nyou're done.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Ffactory_bot_instrumentation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhausgold%2Ffactory_bot_instrumentation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhausgold%2Ffactory_bot_instrumentation/lists"}