{"id":48049152,"url":"https://github.com/lab34-es/lab34-flows","last_synced_at":"2026-04-04T14:20:02.335Z","repository":{"id":320485610,"uuid":"1060035276","full_name":"lab34-es/lab34-flows","owner":"lab34-es","description":"Test automation for local and remote environments with AI capabilities","archived":false,"fork":false,"pushed_at":"2025-09-24T18:10:30.000Z","size":359,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-24T04:25:34.201Z","etag":null,"topics":["test-automation","testing","testing-tools"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/lab34-es.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-19T09:41:53.000Z","updated_at":"2025-09-24T18:10:33.000Z","dependencies_parsed_at":"2025-10-24T04:35:42.454Z","dependency_job_id":null,"html_url":"https://github.com/lab34-es/lab34-flows","commit_stats":null,"previous_names":["lab34-es/lab34-flows"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/lab34-es/lab34-flows","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lab34-es%2Flab34-flows","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lab34-es%2Flab34-flows/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lab34-es%2Flab34-flows/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lab34-es%2Flab34-flows/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lab34-es","download_url":"https://codeload.github.com/lab34-es/lab34-flows/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lab34-es%2Flab34-flows/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31402299,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["test-automation","testing","testing-tools"],"created_at":"2026-04-04T14:20:01.685Z","updated_at":"2026-04-04T14:20:02.326Z","avatar_url":"https://github.com/lab34-es.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flows\n\nHeavily opinionated tool to help you test E2E flows and behaviours.\n\n\u003e Trigger, understand and test E2E flows and behaviours.\n\u003e Ask an AI to generate a flow for you.\n\u003e Run on any environment (staging, production, etc).\n\u003e From Async protocols, APIs and databases, to web applications.\n\nFeatures:\n\n- Integrates with Google Gemini AI to generate test scenarios.\n- Define flows in YAML format that can be shared across members and teams. (no need to explain failing cases verbally).\n- Generate flows automatically using AI with natural language prompts.\n- Define test cases for each step in a way that can be used for CI/CD automation.\n- Mimic and customise the behaviour of dependant applications. (i.e. fail scenarios under user defined circusnstances).\n- Test with randomly generated data - on each attempt.\n- Integrates with MQTT, HTTP apis, PostgreSQL databases and web applications.\n- Keeps environment variables in a safe place.\n- Offers a comprehensive view of the flowing information (headers, body, etc).\n- Built-in replacers for customising requests and responses.\n- Retry / delay mechanism for failed steps.\n- Test flows locally, mimicing dependent applications, without leaving the comform of your localhost.\n\n## Table of contents\n\n- [Flows tools](#flows-tool)\n  - [Table of contents](#table-of-contents)\n  - [General info](#general-info)\n  - [Setup](#setup)\n  - [Usage](#usage)\n    - [AI Mode](#ai-mode)\n  - [Flows](#flows)\n  - [Tests](#tests)\n  - [Playwright](#playwright) (browser automation - experimental)\n  - [Replacers](#replacers)\n  - [Environment variables](#environment-variables)\n\n## General info\n\nThis tool is intended to help you test E2E flows and behaviours.\n\nThe list of steps to execute are defined is files called \"flows\".\n\nThis files are written in YAML format, and are located in the `flows` folder in this repository. Though, you can have your own flows in your own computer / repository.\n\nThe tool will read the flows, and execute the steps in the order they are defined.\n\nAdditionally:\n1. You can mimic the behaviour of dependant applications, by adding a *mimic* section to each step's definition.\n2. You can define test cases for each step, and the tool will execute them.\n\nEn example of a flow file is:\n\n```yaml\nsteps:\n  - application: \"accounting\"\n    method: \"getInvoice\"\n    parameters:\n      params:\n        customerId: \"{{ randomInt0_100 }}\"\n      query:\n        from: \"2023-01-01\"\n        to: \"2023-01-31\"\n      headers:\n        X-Vendor-Name: \"ACME\"\n    mimic:\n      - application: \"marosavat\"\n        url:\n          - \"taxes\"\n      - application: \"coinscrap\"\n        url: \"/frau-detection\"\n        conditions:\n          fraudForCustomer:\n            - \"57\"\n    test: \n      status: 404\n      headers:\n        - Content-Type: \"application/json\"\n      body:\n        error:\n          httpStatusCode: 404\n          code: \"ACCOUNTING_FRAUD_DETECTED\"\n          message: \"Fraud detected in accounting for customer 57\"\n```\n\n## Setup\n\n```bash\n# Install this repository, globally.\n\nnpm install -g @lab34/flows\n\n# Extend NODE_PATH to npm's root, so your application scripts can access the library:\n# On Linux / MacOS:\nexport NODE_PATH=$(npm root -g)\n\n# On Windows:\nset NODE_PATH=%AppData%\\npm\\node_modules\n\n# On NVM for Windows:\nset NODE_PATH=%NVM_SYMLINK%\\node_modules\n\n# On NVM for Windows (powershell)\n$env:NODE_PATH = \"$env:NVM_SYMLINK\\node_modules\"\n```\n\n## Usage\n\nThe Lab34 Flows CLI tool provides a professional command-line interface for running flow definitions from YAML files.\n\n### Usage\n\n```bash\nlab34-flows --help\nlab34-flows --file \u003cpath-to-yaml-file\u003e --env \u003cenvironment\u003e [--debug] [--help]\nlab34-flows --capabilities\nlab34-flows --ai \"\u003cprompt\u003e\"\n```\n\n### Options\n\n|Parameter|Description|\n|-|-|\n|`--file`|Path to the YAML flow definition file (required if not using --ai)|\n|`--ai`|Generate a flow from a prompt using AI (required if not using --file)|\n|`--env`|Environment to run the flow in (required for --file, optional for --ai)|\n|`--debug`|Print debug information including environment variables and Node.js variables|\n|`--help`|Show help information|\n\n### Examples\n\nDisplay help information:\n```bash\nlab34-flows --help\n```\n\nRun a flow with debug information:\n```bash\nlab34-flows --file flows/my-flow.yaml --env production --debug\n```\n\nGenerate and run a flow using AI:\n```bash\nlab34-flows --ai \"Test login functionality with valid credentials\"\n```\n\n### AI Mode\n\nThe AI mode allows you to generate flow definitions using natural language prompts. This feature leverages Google's Generative AI (Gemini) to create YAML flow definitions based on your description of the testing scenario.\n\n#### AI Configuration\n\nBefore using the AI feature, you need to set up your AI configuration:\n\n1. Create a file named `ai.json` in the `~/flows/config/` directory with the following structure:\n\n```json\n{\n  \"defaultProvider\": \"gemini\",\n  \"gemini\": {\n    \"apiKey\": \"YOUR_GEMINI_API_KEY_HERE\",\n    \"model\": \"gemini-pro\",\n    \"temperature\": 0.7,\n    \"topP\": 0.95,\n    \"topK\": 40\n  }\n}\n```\n\n2. Replace `YOUR_GEMINI_API_KEY_HERE` with your actual Gemini API key.\n\n#### Using AI Mode\n\nOnce configured, you can use the AI mode to generate flows:\n\n```bash\nlab34-flows --ai \"Test the user registration process with valid data\"\n```\n\nThe tool will:\n1. Send your prompt to the AI service\n2. Generate a YAML flow definition based on your description\n\nThis feature is particularly useful for:\n- Quickly creating test flows without manually writing YAML\n- Exploring different testing scenarios\n- Generating comprehensive test cases from simple descriptions\n\n### Debug Mode\n\nWhen the `--debug` flag is used, the CLI tool will print detailed information about:\n\n1. All environment variables\n2. Node.js variables including:\n   - `__dirname`: The directory name of the current module\n   - `__filename`: The file name of the current module\n   - `process.cwd()`: The current working directory\n   - `process.argv`: The command line arguments\n\nThis information is useful for troubleshooting and understanding the execution environment.\n\n## Replacers\n\nReplacers are used to customize the requests and responses of the steps in the flow, as well as the mimicked applications. The tool uses Handlebars templates for replacements.\n\nFor example, you can define a value like `{{ randomInt0_100 }}` in the flow file, or in the mimicked application responses, and the tool will replace it with a randomly generated integer between 0 and 100.\n\n### Basic Replacers\n\nThe following basic replacers are available:\n\n| Replacer          | Description                                      | Example                          |\n|-------------------|--------------------------------------------------|----------------------------------|\n| `timestamp`       | Current timestamp in milliseconds                | `1633024800000`                  |\n| `datetime`        | Current date and time in ISO format              | `2023-10-01T12:00:00.000Z`       |\n| `randomInt`       | Random integer between 0 and 999                 | `42`                             |\n| `randomInt0_5`    | Random integer between 0 and 4                   | `3`                              |\n| `randomInt0_10`   | Random integer between 0 and 9                   | `7`                              |\n| `randomInt0_100`  | Random integer between 0 and 99                  | `56`                             |\n| `randomInt0_200`  | Random integer between 0 and 199                 | `123`                            |\n| `randomInt0_300`  | Random integer between 0 and 299                 | `250`                            |\n| `randomInt0_500`  | Random integer between 0 and 499                 | `400`                            |\n| `randomInt0_1000` | Random integer between 0 and 999                 | `789`                            |\n| `randomInt0_9999` | Random integer between 0 and 9998                | `6789`                           |\n| `randomInt0_2000` | Random integer between 0 and 1999                | `1500`                           |\n| `randomInt0_3000` | Random integer between 0 and 2999                | `2500`                           |\n| `randomInt0_4000` | Random integer between 0 and 3999                | `3500`                           |\n| `randomInt0_5000` | Random integer between 0 and 4999                | `4500`                           |\n| `uuid`            | Random UUID                                      | `123e4567-e89b-12d3-a456-426614174000` |\n| `randomPostmanId` | Random 6-digit integer                           | `123456`                         |\n\n### Personal Data Replacers\n\n| Replacer              | Description                                  | Example                          |\n|-----------------------|----------------------------------------------|----------------------------------|\n| `randomEmail`         | Randomly generated email address             | `user123@example.com`            |\n| `randomName`          | Random person full name                      | `John Doe`                       |\n| `randomPersonName`    | Random person first name                     | `Jane`                           |\n| `randomPersonSurname` | Random person last name                      | `Smith`                          |\n| `randomPersonPrefix`  | Random person name prefix                    | `Mr.`                            |\n| `phoneIntl`           | Random phone number in international format  | `+1 555-123-4567`                |\n| `randomString`        | Random alphanumeric string (10 characters)   | `a1b2c3d4e5`                     |\n\n### Location and Company Replacers\n\n| Replacer              | Description                                  | Example                          |\n|-----------------------|----------------------------------------------|----------------------------------|\n| `belgianCityEn`       | Random Belgian city name in English          | `Brussels`                       |\n| `randomCompanyName`   | Random company name                          | `Acme Corporation`               |\n| `randomStreet`        | Random street name                           | `Main Street`                    |\n| `randomStreetNumber`  | Random street number between 0 and 199       | `42`                             |\n| `randomPostalCode`    | Random 4-digit postal code                   | `1000`                           |\n\n### Time-Related Functions\n\nYou can generate dates and timestamps in the past using the following helper functions:\n\n```text\ntimeAgo amount lapse \ntimestampAgo amount lapse \ntsAgo amount lapse \n```\n\nWhere:\n- `amount`: The number of time units to go back\n- `lapse`: The time unit (ms, seconds, minutes, hours, days, months, years)\n\nExamples:\n```text\ntimeAgo 5 \"days\"       \u003c!-- Returns a Date object 5 days in the past --\u003e\ntimestampAgo 2 \"hours\" \u003c!-- Returns a timestamp in milliseconds from 2 hours ago --\u003e\ntsAgo 1 \"month\"        \u003c!-- Returns a formatted timestamp (YYYYMMDDHHMMSS) from 1 month ago --\u003e\n```\n\n### Barcode Generation\n\nYou can generate random barcodes using the barcode helper:\n\n```javascript\nbarcode([pattern])\n```\n\nWhere:\n- `pattern`: is an array of strings and/or numbers.\n  - An string is a fixed value.\n  - A number adds N number of numbers to the barcode.\n\nExamples:\n```javascript\nbarcode([\"123456\", 3, \"789\"]) // Generates a barcode like \"123456123789\"\nbarcode([\"HELLO-\", 4, \"-WORLD\"]) // Generates a barcode like \"HELLO-7832-WORLD\"\n```\n\n### Random Selection\n\nYou can select a random element from an array using the oneOf helper:\n\n```javascript\noneOf([array])\n```\n\nYou can contribute and add more replacers by modifying the `src/helpers/replacer.js` file.\n\n## Flows\n\nFlows are defined in YAML format. Basic examples are available in the `flows` folder of the repository.\n\nThough, you can have your own flows in your own computer / repository, and share them with your team.\n\n## Tests\n\nTests are defined in the flow file, and are executed by the tool.\n\nYou can test two aspects on each step:\n\n1. The it has returned the expected status code and body. (i.e. 200, 404, etc, with certain body contents).\n2. Lantent applications. See [Latent Applications](#latent-applications) for more information.\n\n### JavaScript Expressions in Tests\n\nYou can use JavaScript expressions in your test definitions to perform dynamic validations beyond simple equality checks. To use an expression, prefix it with `$expr:` followed by valid JavaScript code, where `value` represents the actual value being tested.\n\nExamples:\n\n```yaml\ntest:\n  body:\n    count: \"$expr: value \u003e 10\"              # Validates that count is greater than 10\n    status: \"$expr: value === 'completed'\"  # Validates that status equals 'completed'\n    items: \"$expr: Array.isArray(value) \u0026\u0026 value.length \u003e= 3\"  # Validates items is an array with at least 3 elements\n    user:\n      age: \"$expr: value \u003e= 18 \u0026\u0026 value \u003c= 65\"  # Validates age is between 18 and 65\n    timestamp: \"$expr: new Date(value).getFullYear() === 2023\"  # Validates year is 2023\n```\n\nCommon validation scenarios:\n\n| Validation Type | Expression Example |\n|----------------|-------------------|\n| Greater than | `$expr: value \u003e 0` |\n| Equals specific value | `$expr: value === 2` |\n| In a range | `$expr: value \u003e= 5 \u0026\u0026 value \u003c= 10` |\n| String contains | `$expr: typeof value === 'string' \u0026\u0026 value.includes('success')` |\n| Array has items | `$expr: Array.isArray(value) \u0026\u0026 value.length \u003e 0` |\n| Property exists | `$expr: typeof value === 'object' \u0026\u0026 'id' in value` |\n| Date validation | `$expr: new Date(value) \u003e new Date('2023-01-01')` |\n\nThis feature allows for powerful and flexible test assertions without having to modify the testing code.\n\n## Latent Applications\n\n**Only works for MQTT at the moment**\n\nThis are applications that are triggered asynchonously by some step in the flow, for which test validation is important in order to consider a flow as successful.\n\nFor example: you might want to validate the, upon triggering a HTTP request, a certain MQTT has been produced at some point. \n\nTo do so, you can define this applications in the `latentApplications` section in the flow.\n\n```yaml\nlatentApplications:\n  - application: \"mqtt\"\n    client: \"client1\"\n    connection:\n      host: \"1234567890-ats.iot.eu-west-1.amazonaws.com\"\n      key: \"/Users/myuser/mqtt-credentials/private.key\"\n      cert: \"/Users/myuser/mqtt-credentials/cert.crt\"\n      ca: \"/Users/myuser/mqtt-credentials/ca1.pem\"\n    subscribe:\n      - topic: \"client/1\"\n\nsteps: \n  ...\n```\n\nIn this example, we are ensuring we will have a MQTT client connected to the given host, and subscribed to a list of topics, before the flow starts.\n\nThis enables the flow to be able to test that the MQTT messages are being produced as expected.\n\nFor example, in the following example, we are ensuring that a MQTT message containing `status = switched_to_on` is received in the `client/1` topic by the client described above.\n\n```yaml\nsteps:\n  - application: \"\u003csome application\u003e\"\n    method: \"\u003csome method\u003e\"\n    test:\n      latentApplications:\n        - application: \"mqtt\"\n          client: \"client1\"\n          test:\n            - topic: \"client/1\"\n              message: \n                status: \"switched_to_on\"\n          retry:\n            attempts: 1\n            delay: 1\n```\n\n## Playwright\n\nTesting web applications is possible with the tool, as it integrates with [Playwright](https://playwright.dev).\n\nPlaywright automations have their own YAML files, as you can see in [an actual example](src/applications/playful_website/playwright.example.yaml).\n\nTo integrate with Playwright, the application's code must make use of `playwright.run` and pass a playwright yaml file to it.\n\n### Configuration Options\n\nThe Playwright integration now supports enhanced configuration through the YAML file:\n\n```yaml\n# Browser configuration\nbrowserType: chromium  # Options: chromium, firefox, webkit\ndevice: Desktop Chrome # Any device from playwright.devices\nkeepOpen: true  # Keep the browser open after execution (only for debugging purposes)\n\n# Launch options\nlaunchOptions:\n  headless: false\n  ignoreHTTPSErrors: true\n  timeout: 30000\n  args: []  # Additional browser arguments\n\n# Context options\ncontextOptions:\n  locale: en-US\n  timezoneId: Europe/Madrid\n  permissions: []\n  geolocation: null\n  viewport:\n    width: 1280\n    height: 720\n```\n\n### Available Methods\n\n|Method|Description|Parameters|\n|-|-|-|\n|goto|Navigate to a URL|`url`, `waitUntil`, `timeout`|\n|click|Clicks in a element|`selector`, `button`, `clickCount`, `delay`, `timeout`|\n|type|Types a text in a form field|`selector`, `text`, `delay`, `timeout`|\n|fill|Fill a form field|`selector`, `value`, `timeout`|\n|press|Press a key|`selector`, `key`, `delay`, `timeout`|\n|hover|Hover over an element|`selector`, `position`, `timeout`|\n|dragAndDrop|Drag and drop operation|`source`, `target`, `force`, `timeout`|\n|selectOption|Select dropdown option|`selector`, `values`, `timeout`|\n|check|Check a checkbox|`selector`, `position`, `timeout`|\n|uncheck|Uncheck a checkbox|`selector`, `position`, `timeout`|\n|evaluate|Execute JavaScript|`pageFunction`, `arg`|\n|keyboard|Keyboard actions|`action`, `args`|\n|mouse|Mouse actions|`action`, `args`|\n|waitForTimeout|Wait for time|`time`|\n|waitForSelector|Wait for element|`selector`|\n|assertTitle|Assert page title|`title`|\n|screenshot|Take screenshot|`path`|\n|waitForInput|Wait for user input|-|\n|scrape|Extract page data|`selector`, `output`|\n\n### Method Examples\n\n#### goto\n```yaml\nmethod: goto\nparameters:\n  url: \"https://example.com\"\n  waitUntil: \"networkidle\"  # Options: load, domcontentloaded, networkidle\n  timeout: 30000\n```\n\n#### click\n```yaml\nmethod: click\nparameters:\n  selector: \"#submit-button\"\n  button: \"left\"  # Options: left, right, middle\n  clickCount: 1\n  delay: 100\n  timeout: 5000\n```\n\n#### type\n```yaml\nmethod: type\nparameters:\n  selector: \"#search\"\n  text: \"Búsqueda de ejemplo\"\n  delay: 50  # Milisegundos entre pulsaciones\n  timeout: 5000\n```\n\n#### fill\n```yaml\nmethod: fill\nparameters:\n  selector: \"#username\"\n  value: \"user123\"\n  timeout: 5000\n```\n\n#### press\n```yaml\nmethod: press\nparameters:\n  selector: \"#search-input\"\n  key: \"Enter\"\n  delay: 100\n  timeout: 5000\n```\n\n#### hover\n```yaml\nmethod: hover\nparameters:\n  selector: \".dropdown-menu\"\n  position: { x: 0, y: 0 }  # Coordenadas relativas al elemento\n  timeout: 5000\n```\n\n#### dragAndDrop\n```yaml\nmethod: dragAndDrop\nparameters:\n  source: \"#draggable\"\n  target: \"#droppable\"\n  force: true\n  timeout: 5000\n```\n\n#### selectOption\n```yaml\nmethod: selectOption\nparameters:\n  selector: \"#country-select\"\n  values: [\"ES\"]  # Puede ser un string único o un array\n  timeout: 5000\n```\n\n#### check\n```yaml\nmethod: check\nparameters:\n  selector: \"#terms-checkbox\"\n  position: { x: 5, y: 5 }  # Opcional: coordenadas específicas para el click\n  timeout: 5000\n```\n\n#### uncheck\n```yaml\nmethod: uncheck\nparameters:\n  selector: \"#newsletter-checkbox\"\n  position: { x: 5, y: 5 }\n  timeout: 5000\n```\n\n#### dblclick\n```yaml\nmethod: dblclick\nparameters:\n  selector: \"#edit-field\"\n  timeout: 5000\n```\n\n#### focus\n```yaml\nmethod: focus\nparameters:\n  selector: \"#email-input\"\n  timeout: 5000\n```\n\n#### evaluate\n```yaml\nmethod: evaluate\nparameters:\n  pageFunction: \"() =\u003e document.title\"\n  arg: null\n```\n\n#### keyboard\n```yaml\nmethod: keyboard\nparameters:\n  action: \"type\"\n  args: [\"Hello World\", { delay: 100 }]\n```\n\n#### mouse\n```yaml\nmethod: mouse\nparameters:\n  action: \"move\"\n  args: [100, 200]  # x, y coordinates\n```\n\n#### waitForTimeout\n```yaml\nmethod: waitForTimeout\nparameters:\n  time: 1000  # Milisegundos\n```\n\n#### waitForSelector\n```yaml\nmethod: waitForSelector\nparameters:\n  selector: \".loading-indicator\"\n  state: \"hidden\"  # Options: 'attached', 'detached', 'visible', 'hidden'\n  timeout: 30000\n```\n\n#### assertTitle\n```yaml\nmethod: assertTitle\nparameters:\n  title: \"Página de inicio\"\n```\n\n#### screenshot\n```yaml\nmethod: screenshot\nparameters:\n  path: \"screenshots/error-state.png\"\n  fullPage: true  # Opcional: captura toda la página\n  omitBackground: false  # Opcional: fondo transparente\n```\n\n#### waitForInput\n```yaml\nmethod: waitForInput\n# No requiere parámetros - espera entrada del usuario\n```\n\n#### scrape\n```yaml\nmethod: scrape\nparameters:\n  title:\n    selector: \"h1\"\n    output: \"string\"\n  price:\n    selector: \".price\"\n    output: \"number\"\n  isAvailable:\n    selector: \".stock-status\"\n    output: \"boolean\"\n  publishDate:\n    selector: \".publish-date\"\n    output: \"date\"\n```\n\n## Environment variables\n\nTo prevent storing credentials and others in the repository, the testers must keep environment files for each supported application in the application folder itself.\n\ni.e. for an application called ABC, the configuration files must be stored in `~/.../applications/abc/envs/ac1.env`.\n\n### PostgreSQL Database Configuration\n\nThe PostgreSQL client supports flexible configuration through environment variables. You can either use a connection string or individual parameters.\n\n#### Option 1: Connection String (Recommended for simplicity)\n\n```bash\nDATABASE_CONNECTION_STRING=postgres://user:password@host:5432/database\n```\n\n#### Option 2: Individual Parameters (Recommended for flexibility)\n\n| Environment Variable | Description | Example |\n|---------------------|-------------|---------|\n| `PGUSER` | Database user | `myuser` |\n| `PGPASSWORD` | Database password | `mypassword` |\n| `PGHOST` | Database host | `localhost` or `db.example.com` |\n| `PGPORT` | Database port | `5432` |\n| `PGDATABASE` | Database name | `mydatabase` |\n| `PGQUERY_TIMEOUT` | Query timeout in milliseconds | `30000` |\n| `PGLOCK_TIMEOUT` | Lock timeout in milliseconds | `10000` |\n| `PGCLIENT_ENCODING` | Client character encoding | `UTF8` |\n| `PGOPTIONS` | Command-line options for the server | `-c statement_timeout=30s` |\n\n#### Configuration Priority\n\n1. If `DATABASE_CONNECTION_STRING` is provided, it takes precedence (for backward compatibility)\n2. Otherwise, individual parameters (`PGUSER`, `PGHOST`, etc.) are used\n3. Additional parameters (`PGQUERY_TIMEOUT`, `PGLOCK_TIMEOUT`, etc.) work with both approaches\n\n#### SSL Configuration (Optional)\n\nThe PostgreSQL client supports SSL connections with flexible configuration options:\n\n| Environment Variable | Description | Example |\n|---------------------|-------------|---------|\n| `PGSSL_ENABLED` | Enable SSL connection | `true` or `false` |\n| `PGSSL_REJECT_UNAUTHORIZED` | Reject unauthorized certificates | `true` or `false` |\n| `PGSSL_CA` | Path to CA certificate file | `/path/to/ca.pem` |\n| `PGSSL_CERT` | Path to client certificate file | `/path/to/client-cert.pem` |\n| `PGSSL_KEY` | Path to client key file | `/path/to/client-key.pem` |\n\n#### Examples\n\n**Using connection string:**\n```bash\nDATABASE_CONNECTION_STRING=postgres://admin:secret@db.example.com:5432/production\nPGQUERY_TIMEOUT=60000\n```\n\n**Using individual parameters:**\n```bash\nPGUSER=admin\nPGPASSWORD=secret\nPGHOST=db.example.com\nPGPORT=5432\nPGDATABASE=production\nPGQUERY_TIMEOUT=60000\nPGLOCK_TIMEOUT=30000\n```\n\n**Local development example:**\n```bash\nPGUSER=developer\nPGPASSWORD=localpass\nPGHOST=localhost\nPGPORT=5432\nPGDATABASE=testdb\n```\n\n**SSL connection with self-signed certificates (development):**\n```bash\nPGUSER=developer\nPGPASSWORD=devpass\nPGHOST=secure.dev.example.com\nPGPORT=5432\nPGDATABASE=devdb\nPGSSL_ENABLED=true\nPGSSL_REJECT_UNAUTHORIZED=false\n```\n\n**SSL connection with full certificate validation (production):**\n```bash\nDATABASE_CONNECTION_STRING=postgres://admin:secret@secure.db.example.com:5432/production\nPGSSL_ENABLED=true\nPGSSL_CA=/path/to/ca-certificate.pem\nPGSSL_CERT=/path/to/client-certificate.pem\nPGSSL_KEY=/path/to/client-key.pem\n```\n\n**SSL with custom CA certificate only:**\n```bash\nPGUSER=admin\nPGPASSWORD=secret\nPGHOST=db.example.com\nPGPORT=5432\nPGDATABASE=production\nPGSSL_ENABLED=true\nPGSSL_CA=/path/to/custom-ca.pem\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flab34-es%2Flab34-flows","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flab34-es%2Flab34-flows","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flab34-es%2Flab34-flows/lists"}