diff --git a/packages/qa-toolkit-br/0.1.0/README.md b/packages/qa-toolkit-br/0.1.0/README.md new file mode 100644 index 00000000..184d50e5 --- /dev/null +++ b/packages/qa-toolkit-br/0.1.0/README.md @@ -0,0 +1,76 @@ +# QA Toolkit BR + +Snippets for **Quality Assurance** work, focused on Brazilian test data. Type a trigger and Espanso pastes freshly generated test data: valid CPF/CNPJ, names, phones, UUIDs, tokens, JWTs, Lorem ipsum, edge-case inputs and bug report templates. + +> **Português:** atalhos para QA que geram CPF/CNPJ válidos (fake), nomes, celulares, UUID, token, JWT, Lorem ipsum, payloads de teste e templates de bug report. + +## Requirements + +Most generators run a small **Python 3** script (standard library only, no extra packages). + +- `python3` must be available in your `PATH`. +- **Windows:** if your Python is called `python` or `py`, replace `python3` with that command in `package.yml`. + +All snippets use `force_mode: clipboard` so the value is pasted at once. This avoids problems with input masks in web forms. + +## Triggers + +### Brazilian documents (check digits are valid) + +| Trigger | Example | +|---|---| +| `:fakecpf` | `123.456.789-09` | +| `:fakencpf` | `12345678909` (digits only) | +| `:fakecnpj` | `12.345.678/0001-95` | +| `:fakencnpj` | `12345678000195` (digits only) | + +### Personal data + +| Trigger | Output | +|---|---| +| `:fakenome` | Random Brazilian full name | +| `:fakeemail` | Unique address at `@example.com` (reserved domain, never delivers) | +| `:fakecel` | Mobile number, e.g. `(11) 98765-4321` | +| `:fakecep` | Real CEP from a state capital, e.g. `01001-000` | +| `:fakeplaca` | Mercosul license plate, e.g. `ABC1D23` | +| `:fakecartao` | Visa test card number that passes the Luhn check | +| `:fakesenha` | Strong 16-character password | + +### Identifiers + +| Trigger | Output | +|---|---| +| `:uuid` | UUID v4 | +| `:token` | Random URL-safe token (32 bytes) | +| `:jwt` | HS256 JWT valid for 1 hour, signed with the key `qa-secret` | + +### Date and time + +| Trigger | Output | +|---|---| +| `:iso` | Current local time in ISO 8601, e.g. `2026-10-21T14:30:00-03:00` | +| `:epoch` | Current Unix timestamp | + +### Test inputs + +| Trigger | Output | +|---|---| +| `:lorem` | One paragraph of Lorem ipsum | +| `:lipsum3` | Three paragraphs of Lorem ipsum | +| `:max255` | Exactly 255 characters, for field length limits | +| `:unicode` | Accents, CJK, Arabic, emoji, quotes and HTML characters | +| `:xss` | `` | +| `:sqli` | `' OR '1'='1' -- ` | + +### Templates (Portuguese) + +| Trigger | Output | +|---|---| +| `:bug` | Bug report template with today's date | +| `:gherkin` | BDD scenario (Funcionalidade / Cenário / Dado / Quando / Então) | + +## Disclaimer + +All generated data is fake and intended **only for software testing**. The CPF, CNPJ and card numbers are mathematically valid but random. Do not use them to impersonate people or companies, or for fraud. + +The `:xss` and `:sqli` payloads are classic test strings. Only use them on systems you are authorized to test. diff --git a/packages/qa-toolkit-br/0.1.0/_manifest.yml b/packages/qa-toolkit-br/0.1.0/_manifest.yml new file mode 100644 index 00000000..a7ab700d --- /dev/null +++ b/packages/qa-toolkit-br/0.1.0/_manifest.yml @@ -0,0 +1,7 @@ +name: "qa-toolkit-br" +title: "QA Toolkit BR" +description: "Test data for QA: valid CPF/CNPJ, names, phones, UUID, tokens, JWT, Lorem ipsum, edge-case inputs and bug report templates." +version: "0.1.0" +author: "ChristoferRSouza" +tags: ["qa", "testing", "test-data", "fake", "brazil", "cpf", "cnpj", "uuid", "jwt", "lorem"] +homepage: "https://github.com/ChristoferRSouza" diff --git a/packages/qa-toolkit-br/0.1.0/package.yml b/packages/qa-toolkit-br/0.1.0/package.yml new file mode 100644 index 00000000..2f8b40bb --- /dev/null +++ b/packages/qa-toolkit-br/0.1.0/package.yml @@ -0,0 +1,344 @@ +# qa-toolkit-br - Espanso package with test data generators for QA +# Requires Python 3 available as `python3` in PATH (see README.md) + +matches: + # ---------------- DOCUMENTOS ---------------- + - trigger: ":fakecpf" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + while True: + n = [random.randint(0, 9) for _ in range(9)] + if len(set(n)) > 1: + break + for w in (10, 11): + r = sum(d * (w - i) for i, d in enumerate(n)) % 11 + n.append(0 if r < 2 else 11 - r) + s = "".join(map(str, n)) + print(f"{s[:3]}.{s[3:6]}.{s[6:9]}-{s[9:]}", end="") + + - trigger: ":fakencpf" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + while True: + n = [random.randint(0, 9) for _ in range(9)] + if len(set(n)) > 1: + break + for w in (10, 11): + r = sum(d * (w - i) for i, d in enumerate(n)) % 11 + n.append(0 if r < 2 else 11 - r) + print("".join(map(str, n)), end="") + + - trigger: ":fakecnpj" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + n = [random.randint(0, 9) for _ in range(8)] + [0, 0, 0, 1] + w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] + for w in (w1, [6] + w1): + r = sum(d * p for d, p in zip(n, w)) % 11 + n.append(0 if r < 2 else 11 - r) + s = "".join(map(str, n)) + print(f"{s[:2]}.{s[2:5]}.{s[5:8]}/{s[8:12]}-{s[12:]}", end="") + + - trigger: ":fakencnpj" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + n = [random.randint(0, 9) for _ in range(8)] + [0, 0, 0, 1] + w1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] + for w in (w1, [6] + w1): + r = sum(d * p for d, p in zip(n, w)) % 11 + n.append(0 if r < 2 else 11 - r) + print("".join(map(str, n)), end="") + + # ---------------- DADOS PESSOAIS ---------------- + - trigger: ":fakenome" + replace: "{{nome}} {{sobrenome}} {{sobrenome2}}" + force_mode: clipboard + vars: + - name: nome + type: random + params: + choices: [Ana, Beatriz, Camila, Daniela, Fernanda, Gabriela, Juliana, Larissa, Mariana, Patrícia, Rafaela, Sofia, + André, Bruno, Carlos, Diego, Eduardo, Felipe, Gustavo, João, Lucas, Mateus, Pedro, Rafael, Thiago] + - name: sobrenome + type: random + params: + choices: [Silva, Santos, Oliveira, Souza, Lima, Pereira, Costa, Ferreira, Rodrigues, Almeida, Nascimento, Carvalho] + - name: sobrenome2 + type: random + params: + choices: [Araújo, Barbosa, Cardoso, Gomes, Martins, Melo, Moreira, Ribeiro, Rocha, Teixeira, Vieira, Monteiro] + + - trigger: ":fakeemail" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import uuid + print(f"qa.{uuid.uuid4().hex[:12]}@example.com", end="") + + - trigger: ":fakecel" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + ddd = random.choice([11, 21, 31, 41, 47, 48, 51, 61, 62, 67, 71, 81, 85, 91]) + n = "9" + "".join(str(random.randint(0, 9)) for _ in range(8)) + print(f"({ddd}) {n[:5]}-{n[5:]}", end="") + + - trigger: ":fakecep" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: random + params: + choices: + - "01001-000" # São Paulo - Praça da Sé + - "20040-002" # Rio de Janeiro - Centro + - "30130-010" # Belo Horizonte - Centro + - "40020-000" # Salvador - Comércio + - "70040-010" # Brasília - Asa Norte + - "80010-000" # Curitiba - Centro + - "90010-000" # Porto Alegre - Centro + - "79002-000" # Campo Grande - Centro + + - trigger: ":fakeplaca" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random, string + L = string.ascii_uppercase + p = "".join(random.choices(L, k=3)) + str(random.randint(0, 9)) + random.choice(L) + f"{random.randint(0, 99):02d}" + print(p, end="") + + - trigger: ":fakecartao" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import random + d = [4] + [random.randint(0, 9) for _ in range(14)] + s = 0 + for i, x in enumerate(reversed(d)): + if i % 2 == 0: + x *= 2 + if x > 9: x -= 9 + s += x + d.append((10 - s % 10) % 10) + n = "".join(map(str, d)) + print(" ".join(n[i:i+4] for i in range(0, 16, 4)), end="") + + - trigger: ":fakesenha" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import secrets, string + grupos = [string.ascii_lowercase, string.ascii_uppercase, string.digits, "!@#$%&*?-_"] + s = [secrets.choice(g) for g in grupos] + [secrets.choice("".join(grupos)) for _ in range(12)] + secrets.SystemRandom().shuffle(s) + print("".join(s), end="") + + # ---------------- IDENTIFICADORES ---------------- + - trigger: ":uuid" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: [python3, -c, "import uuid; print(uuid.uuid4(), end='')"] + + - trigger: ":token" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: [python3, -c, "import secrets; print(secrets.token_urlsafe(32), end='')"] + + - trigger: ":jwt" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: + - python3 + - -c + - | + import base64, hashlib, hmac, json, time, uuid + b = lambda x: base64.urlsafe_b64encode(x).rstrip(b"=").decode() + now = int(time.time()) + h = b(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode()) + p = b(json.dumps({"sub": str(uuid.uuid4()), "name": "QA Tester", "role": "qa", + "iat": now, "exp": now + 3600}, separators=(",", ":")).encode()) + sig = b(hmac.new(b"qa-secret", f"{h}.{p}".encode(), hashlib.sha256).digest()) + print(f"{h}.{p}.{sig}", end="") + + # ---------------- DATA E HORA ---------------- + - trigger: ":iso" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: [python3, -c, "import datetime; print(datetime.datetime.now().astimezone().isoformat(timespec='seconds'), end='')"] + + - trigger: ":epoch" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: [python3, -c, "import time; print(int(time.time()), end='')"] + + # ---------------- TEXTOS E ENTRADAS DE TESTE ---------------- + - trigger: ":lorem" + force_mode: clipboard + replace: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + + - trigger: ":lipsum3" + force_mode: clipboard + replace: |- + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. + + - trigger: ":max255" + replace: "{{v}}" + force_mode: clipboard + vars: + - name: v + type: script + params: + args: [python3, -c, "print(('0123456789' * 26)[:255], end='')"] + + - trigger: ":unicode" + force_mode: clipboard + replace: "Ação Ñandú Ünïcødé 日本語 中文 한국어 العربية ✓ 🚀🔥 \"aspas\" 'simples' & % $ #" + + - trigger: ":xss" + force_mode: clipboard + replace: "" + + - trigger: ":sqli" + force_mode: clipboard + replace: "' OR '1'='1' -- " + + # ---------------- TEMPLATES ---------------- + - trigger: ":bug" + force_mode: clipboard + replace: |- + **Título:** + **Data:** {{hoje}} + **Ambiente:** (produção / homologação / dev) - versão: + **Navegador / Dispositivo:** + **Severidade:** (crítica / alta / média / baixa) + + **Pré-condições:** + - + + **Passos para reproduzir:** + 1. + 2. + 3. + + **Resultado esperado:** + + + **Resultado obtido:** + + + **Evidências:** (prints, vídeos, logs) + vars: + - name: hoje + type: date + params: + format: "%d/%m/%Y" + + - trigger: ":gherkin" + force_mode: clipboard + replace: |- + Funcionalidade: + + Cenário: + Dado que + E + Quando + Então + E