@przeslijmi/real-fake-data-playwright
v1.18.0
Published
Playwright fixtures for Real Fake Data — 327 seeded, realistic test-data generators plus a compose() method that seeds a whole nested dataset in one call: person & company names across 27 EU countries, the United States and Canada, one-call whole people a
Maintainers
Readme
@przeslijmi/real-fake-data-playwright
Playwright fixtures for Real Fake Data — 327 generators of realistic, synthetic test data, one typed method per record:
- Person & company names and email addresses across 29 countries (the 27 EU member states plus the US and Canada) —
dePersonName,itCompanyName,usPersonName,caCompanyName,plEmail, … in the local script and inflection (names romanised to ASCII for emails, with free, regional, and company-derived domains), plus multi-countrypersonName/companyName/emailthat draw from any mix of countries. - National identifiers and VAT / company numbers for every EU member state — French
frNir/frSiren, ItalianitCodiceFiscale, SpanishesDni/esNie, DanishdkCpr, SwedishsePersonnummer, DutchnlBsn, GermandeSteuerId/deUstIdnr, and 60+ more — each with correct checksums and the sameinvalid/edge/extremetriggers. - One-call whole people and companies for every EU country —
dkPerson,frPerson, … return a consistent name + national number;dkCompany,deCompany,nlCompany, … return a consistent trading name, legal form, and the country's register / tax / VAT numbers, all from one seed. - Vehicle registration plates for all 29 countries —
deVehicleRegistration,frVehicleRegistration,itVehicleRegistration, plususVehicleRegistration(all 51 US jurisdictions) andcaVehicleRegistration(all 13 provinces and territories) — produce realistic plates with each country's own plate kinds (standard, custom/vanity, motorcycle, diplomatic, military, historic, electric, …), region/district codes, era formats, and the sharededge/extremetriggers. Plus multi-countryvehicleRegistration, whosetypespeaks a shared category vocabulary rather than any one country's names —{ type: 'historic' }reaches Hungary'soldtimer, Ireland'svintageand Finland'smuseumalike. - IBANs for all 27 EU countries —
deIban,frIban,itIban, … return a valid IBAN with correct mod-97 check digits (and, where the national account number has its own internal check digit, that too — Italy's CIN, Spain's DC, France's clé RIB, and the rest), embedding a real bank code you can pin bybankCodeorbankName. The sharedinvalid/edge/extremetriggers apply. - Product & service offerings for all 29 countries —
deOffering,frOffering,usOffering,caOffering, … return a localized product/service with a plausible price, unit, and its NACE industry; plus multi-countryofferingthat draws from any mix. Prices are EUR for the EU members, USD for the US and CAD for Canada (not rate-converted), so an unpinned multi-country batch mixes currencies — each record reports its owncurrency. Filter byindustry,type, or full-textofferingName/industryName, with the sharededge/extreme/invalidtriggers. - United States —
usSsn,usEin,usRoutingNumber,usBankAccount,usVehicleRegistration,usPerson,usCompany, plususPersonName/usCompanyName/usEmail/usOffering. Three differ from their EU counterparts by nature:usSsntakes nosex/age options (an SSN encodes neither),usPersonaccepts them but draws the birth date rather than decoding it, andusBankAccountpairs a checksum-valid ABA routing number with a synthetic account number — the US has no national account-number standard to validate against.usRoutingNumber({ realBank: true })returns a genuine published routing number.usVehicleRegistrationcovers all 51 jurisdictions in each state's own format and reportsstate(a US plate doesn't encode it), with Wyoming also reporting itscounty. - Canada —
caSin,caBusinessNumber,caTransitNumber,caBankAccount,caVehicleRegistration,caPerson,caCompany, pluscaPersonName/caCompanyName/caEmail/caOffering. Where the US identifiers lack checksums, Canada's have them: the SIN and Business Number are both Luhn-checked, soinvalidbreaks real arithmetic.caTransitNumberreturns both renderings —XXXXX-YYYon a cheque and0YYYXXXXXfor EFT, whose field order reverses — andcaBankAccountfollows CPA Standard 006 (7 or 12 digits), making it better standardised than its US counterpart. Names and company forms are bilingual (Tremblay,Ltée), and jurisdictions resolve in either language:QC,Quebec,Québec. - The full Polish national set — valid PESELs (correct checksums), NIPs, REGONs, IBANs, KRS and land-register numbers, ID cards, passports, driving licences, and addresses drawn from real cities and streets.
- Synthetic IDs —
uuid(v4/v7),ulid,nanoId,objectId, and auto-incrementsequence: the technical primary keys every record needs, deterministic from the seed (so a seeded UUID is reproducible). - Locale-agnostic — lorem ipsum,
customRegex(a random string matching any regex you supply; needs a free account), and the weightedenum/objectpickers (draw a member or object from a distribution you supply).
Output looks real but is fake — safe for staging, demos, and seed data.
- Random by default. It's a random-data generator: every call returns fresh data on each run. Pin a
seedonly when you want a fixed, reproducible dataset — e.g. to replay exactly what a failing run used. - Three ways in, no lock-in. A zero-config
fakeDatasingleton (import and go), acreateFakeDatabuilder for full control (custom URL, auth headers, seed), or the PlaywrightfakeDatafixture — pick per test, mix freely. - Typed end to end. Two methods per generator (one record, or a batch), fully typed inputs and results.
Install
npm install -D @przeslijmi/real-fake-data-playwright
# or: pnpm add -D @przeslijmi/real-fake-data-playwrightRequires @playwright/test (peer dependency) and Node 18+ (for global fetch).
Quick start
There are three ways to pull data in — pick whichever fits, they all return the same typed records. None is privileged; the fixture is not required.
1. Singleton — import and go
Zero config. The fakeData singleton is pre-wired to the public hosted API and returns fresh random data on every run. Best when you just want data in one (or many) tests without touching your Playwright config.
import { test, expect } from '@playwright/test';
import { fakeData } from '@przeslijmi/real-fake-data-playwright';
test('registers a new customer', async ({ page }) => {
const person = await fakeData.plPerson({ sex: 'f' });
await page.goto('/signup');
await page.getByLabel('First name').fill(person.name);
await page.getByLabel('Surname').fill(person.surname);
await page.getByLabel('PESEL').fill(person.pesel);
await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.getByText(person.surname)).toBeVisible();
});The singleton targets the public endpoint only and sends no auth headers. Need a custom URL, headers, or a pinned seed? Use one of the next two.
2. createFakeData — full control
Build your own instance over any base URL, with auth headers and an optional seed. Best when you self-host the API, need authentication, or want to pin a fixed dataset.
import { test, expect } from '@playwright/test';
import { createFakeData, CloudFakeDataProvider } from '@przeslijmi/real-fake-data-playwright';
const fakeData = createFakeData(
new CloudFakeDataProvider({
baseUrl: 'https://api.real-fake-data.com',
headers: { 'x-api-key': process.env.RFD_API_KEY ?? '' },
}),
{ seed: 42 }, // optional — omit for random
);
test('uses pinned data', async ({ page }) => {
const person = await fakeData.plPerson({ sex: 'f' });
// …
});3. Fixture — Playwright-native
Configure once with test.use(...) and the fakeData fixture is injected into every test in scope. Best when you want the data wired into Playwright's lifecycle and configured per file / project.
import { test, expect } from '@przeslijmi/real-fake-data-playwright';
test.use({ realFakeData: { baseUrl: 'https://api.real-fake-data.com' } });
test('registers a new customer', async ({ page, fakeData }) => {
const person = await fakeData.plPerson({ sex: 'f' });
// …
await expect(page.getByText(person.surname)).toBeVisible();
});Here test and expect are the standard Playwright exports, extended with the fakeData fixture — use them exactly as you would @playwright/test. baseUrl is optional: omit it to use the public hosted API, set it for a self-hosted instance. Like the other two, it's random by default; add seed to pin it (see Configuration).
Configuration
Set options with test.use({ realFakeData: { … } }), at any scope (file, describe, or project, via your Playwright config).
| Option | Type | Description |
| --------- | ------------------------ | ------------------------------------------------------------------------------------------------------- |
| baseUrl | string | Base URL of the Real Fake Data API. Omit to use the public hosted API (https://api.real-fake-data.com); set it for a self-hosted or staging instance. |
| seed | number | Base seed for the test. Omit (the default) for fresh random data on every run; set it to pin the test to a fixed, reproducible dataset. |
| headers | Record<string, string> | Extra headers sent with every request (e.g. an API key, to draw on your account's tokens instead of the shared anonymous lane). |
Random by default, reproducible on demand
By default no seed is set, so every call draws fresh random data on each run — this is a random-data generator, and that's the point. When a test fails on a particular record, log the data (or have your CI capture it) and pin a seed to replay the exact same dataset:
test.use({ realFakeData: { baseUrl, seed: 42 } }); // pin this file to a fixed datasetWith a base seed in play, the Nth call within a test uses seed + N — so the calls stay distinct from one another, yet the whole sequence reproduces identically on the next run. So three back-to-back plPerson() calls still return three different people; re-running with the same seed returns those same three. The same holds for the singleton and createFakeData instances. Pass seed on any individual call to override just that one draw.
Seed a whole dataset — compose()
The generator methods return one record each. compose() fills a whole nested dataset in a single call — users with their orders, orders with their line items, foreign keys that line up. You send a shape skeleton whose slots are marked with $-sources; everything else is a literal.
const order = await fakeData.compose<{
id: string;
customer: string;
items: { sku: string }[];
}>({
shape: {
id: '$generator.any.uuid.value',
customer: '$generator.pl.person.name',
items: {
$count: [1, 3],
sku: '$generator.any.nanoid.value',
},
},
});compose() returns the filled document (the data, unwrapped). The result type is caller-defined — pass a type argument to type it, or leave it unknown. It respects the instance's seed the same way generator calls do: an unseeded fixture returns fresh data each run, a seeded one is reproducible, and a per-call seed in the request pins that draw.
compose() is available on every tier, including without an API key. The full request grammar — $count, $generators/shared for consistency, $date/$anchor for related dates — is documented at real-fake-data.com/seed-a-database.
Generators
Each generator exposes a singular method returning one record and a plural taking count as its first argument and returning an array of that many. Method names are locale-prefixed (plPesel, dePersonName, plEmail, …) so generators for different countries never collide; the locale-agnostic lorem and the multi-country aggregates (personName, companyName, email) carry no prefix.
Every method accepts optional constraints. Pass seed on any call to pin just that draw (overriding the instance's seed sequence when one is set).
Triggers. Beyond each generator's own knobs, three cross-cutting flags shape what kind of data you get. Every generator accepts all three, whether or not its row below lists them; what each does for a given generator is in its description at real-fake-data.com/docs:
edge: true— restrict output to rarely-exercised but still-valid corners (leap-day PESELs, punctuation-heavy company names, boundary serials).invalid: true— return a value that fails validation: a deliberately wrong check digit where the value has a checksum, a deliberately malformed value elsewhere; the rest of the record stays correct.extreme: true— return correct data in a deliberately hostile encoding: untrimmed whitespace (incl. non-breaking spaces), invisible/zero-width characters and a leading BOM, homoglyph lookalikes, or a bidi override / stacked combining marks — one class per value, so a plural call rotates across them. Only the human-facing string is mangled and it stays recoverable after normalisation: identifiers keep their digits ASCII (homoglyphs excluded) and still checksum; names/companies/people mangle the name only, leavinginitials,legalForm, birth dates, and national identifiers clean. ForloremandcustomRegexhomoglyphs are excluded, so the value stays machine-parseable. Use it to test that your pipeline trims, normalises, and compares values safely.
const messy = await fakeData.plPesel({ extreme: true }); // e.g. " 53022100027\n" — trims back to a valid PESEL
const spoof = await fakeData.dePersonName({ extreme: true }); // Cyrillic-lookalike letters; initials stay cleanNames across 29 countries
Every country listed below exposes <cc>PersonName/<cc>PersonNames, <cc>CompanyName/<cc>CompanyNames, <cc>Company/<cc>Companies, <cc>Email/<cc>Emails, and <cc>Offering/<cc>Offerings, where <cc> is its ISO 3166 code: at, be, bg, cy, cz, de, dk, ee, es, fi, fr, gr, hr, hu, ie, it, lt, lu, lv, mt, nl, pl, pt, ro, se, si, sk.
| Singular | Plural | Returns (singular) | Options |
| ----------------------- | ------------------------------- | ------------------------------------------- | -------------------------------------- |
| <cc>PersonName(opts?) | <cc>PersonNames(count, opts?) | { name, surname, initials, sex } | sex, edge, caseStrict |
| <cc>CompanyName(opts?)| <cc>CompanyNames(count, opts?)| { value, legalForm, strategy } | strategy, legalForm, edge |
| <cc>Company(opts?) | <cc>Companies(count, opts?) | { name, legalForm, …national ids } | strategy, legalForm, invalid, edge |
| <cc>Email(opts?) | <cc>Emails(count, opts?) | { value, localPart, domain, pattern, domainCategory, company, plusTag } | domain, domainCategory, pattern, plusTag, exotic, edge |
| <cc>Offering(opts?) | <cc>Offerings(count, opts?) | { value, offeringName, kind, unit, price, currency, industryCode, industryName, language } | industry, type, industryName, offeringName, language, edge, extreme, invalid |
| personName(opts?) | personNames(count, opts?) | { name, surname, initials, sex, country } | sex, edge, caseStrict, countries |
| companyName(opts?) | companyNames(count, opts?) | { value, legalForm, strategy, country } | strategy, edge, countries |
| email(opts?) | emails(count, opts?) | { value, localPart, domain, pattern, domainCategory, company, plusTag, country } | domain, domainCategory, pattern, plusTag, exotic, edge, countries |
| offering(opts?) | offerings(count, opts?) | { value, offeringName, kind, unit, price, currency, industryCode, industryName, language, country } | industry, type, industryName, offeringName, language, edge, extreme, invalid, countries |
| vehicleRegistration(opts?) | vehicleRegistrations(count, opts?) | { country, value, type, category, region? } | type (a shared category), edge, extreme, invalid, countries |
const ceo = await fakeData.dePersonName({ sex: 'f' }); // German given name + surname
const vendor = await fakeData.itCompanyName({ edge: true }); // edge-case Italian company name
const eu = await fakeData.personName({ countries: ['pl', 'sk', 'it'] }); // drawn from one of the three
const inbox = await fakeData.plEmail({ domainCategory: 'corporate' }); // [email protected]
const item = await fakeData.deOffering({ type: 'service' }); // localized German service + price
const cart = await fakeData.offering({ countries: ['pl', 'it'], industry: '56.11' }); // restaurant itemsAn offering is a product or service a business sells: a localized value/offeringName, a kind (product/service), a unit (pc, month, kg, …), a price in EUR minor units (cents) with currency, and the industryCode + localized industryName it belongs to. Pin an industry by NACE code, restrict to type, or full-text filter with offeringName/industryName; language pins the name language (multilingual countries otherwise blend theirs). edge/extreme/invalid apply as elsewhere (invalid cannot combine with edge/extreme).
countries (on the prefix-less personName/companyName/email/offering/vehicleRegistration only) is an array of ISO codes; each record is generated by one country picked from the list. Omit it to draw from all 29 — the 27 EU member states plus the US and Canada. The per-country legalForm values differ by country (e.g. GmbH, S.r.l., S.A.), so they are typed as string; pass 'any' for a weighted-random one or 'none' to omit it. For email, domainCategory: 'corporate' builds the address on a company-derived domain and reports the brand in company; non-Latin names (Cyrillic, Greek) are romanised to ASCII.
Polish national generators
| Singular | Plural | Returns (singular) | Common options |
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------- |
| plPesel(opts?) | plPesels(count, opts?) | { value, birthDate, sex } | sex, atAge, olderThan, youngerThan, bornOn/Before/After, edge, extreme, invalid |
| plPerson(opts?) | plPeople(count, opts?) | { name, surname, initials, birthDate, pesel } | same as plPesel, plus caseStrict |
| plAddress(opts?) | plAddresses(count, opts?) | { buildingNumber, postalCode, cityName, …, terytCodes } | teryt (1–7 digit prefix), live (full current register; default draws the frozen catalogue, so a seed reproduces) |
| plNip(opts?) | plNips(count, opts?) | { value, digits } | format, invalid |
| plIban(opts?) | plIbans(count, opts?) | { value, electronicFormat, bankCode, bankName } | format, bankCode, bankName, invalid |
| plRegon(opts?) | plRegons(count, opts?) | { value, variant } | variant (short/long/any), invalid |
| plCompany(opts?) | plCompanies(count, opts?) | { name, legalForm, nip, regon, krs } | strategy, legalForm, activityPrefix, format, edge, invalid |
| plCompanyName(opts?) | plCompanyNames(count, opts?) | { value, legalForm, strategy } | strategy, legalForm, activityPrefix, edge |
| plIdCard(opts?) | plIdCards(count, opts?) | { value, series, number, expirationDate } | format, expired, invalid |
| plPassport(opts?) | plPassports(count, opts?) | { value, series, number } | format, invalid |
| plKrs(opts?) | plKrsNumbers(count, opts?) | { value, number } | format |
| plLandRegister(opts?) | plLandRegisters(count, opts?) | { value, courtCode, number, checkDigit, court? } | format, court, invalid |
| plDrivingLicense(opts?) | plDrivingLicenses(count, opts?) | { value, serial, year, suffix } | format, year |
| plVehicleRegistration(opts?) | plVehicleRegistrations(count, opts?) | { value, prefix, individualPart, type, … } | type, voivodeship, county, format, edge, extreme |
The Polish plate is also part of the all-29-country vehicle-registration family below.
A Polish person name on its own (no PESEL/birth date) is plPersonName — part of the 29-country table above.
EU national identifiers
Every EU member state exposes its core national-person identifier and its business / VAT numbers. Numbers that encode a birth date and/or sex accept the same person constraints as plPesel (sex, atAge, olderThan, youngerThan, bornOn/bornBefore/bornAfter); the rest expose only their own knobs. Every checksum-bearing number takes invalid (deliberately wrong check digit) and edge (rare-corner values); generators with a VAT/intra-EU rendering take format: 'national' | 'vat' (some use a generator-specific enum — see each row).
France (fr)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| frSiren(opts?) | frSirens(count, opts?) | { value, digits } | format, invalid, edge |
| frNir(opts?) | frNirs(count, opts?) | { value, digits, sex, birthYear, birthMonth } | sex, age/birth filters, invalid, edge |
| frPerson(opts?) | frPeople(count, opts?) | { name, surname, initials, birthDate, nir } | sex, age/birth filters, invalid, edge, caseStrict |
Austria (at)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| atSvnr(opts?) | atSvnrs(count, opts?) | { value, digits, birthDate } | age/birth filters, invalid, edge |
| atPerson(opts?) | atPeople(count, opts?) | { name, surname, initials, birthDate, svnr } | sex, age/birth filters, invalid, edge, caseStrict |
| atUid(opts?) | atUids(count, opts?) | { value, digits } | format, invalid, edge |
| atFirmenbuchnummer(opts?) | atFirmenbuchnummers(count, opts?) | { value, number, letter } | invalid, edge |
| atSteuernummer(opts?) | atSteuernummers(count, opts?) | { value, digits } | edge |
Belgium (be)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| beRijksregisternummer(opts?) | beRijksregisternummers(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, kind, invalid, edge |
| bePerson(opts?) | bePeople(count, opts?) | { name, surname, initials, birthDate, rijksregisternummer } | sex, age/birth filters, invalid, edge, caseStrict |
| beOndernemingsnummer(opts?) | beOndernemingsnummers(count, opts?) | { value, digits } | format, invalid, edge |
Bulgaria (bg)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| bgEgn(opts?) | bgEgns(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| bgPerson(opts?) | bgPeople(count, opts?) | { name, surname, initials, birthDate, egn } | sex, age/birth filters, invalid, edge, caseStrict |
| bgEik(opts?) | bgEiks(count, opts?) | { value, digits } | format, invalid, edge |
Croatia (hr)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| hrOib(opts?) | hrOibs(count, opts?) | { value, digits } | format, invalid, edge |
| hrJmbg(opts?) | hrJmbgs(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| hrPerson(opts?) | hrPeople(count, opts?) | { name, surname, initials, birthDate, jmbg } | sex, age/birth filters, invalid, edge, caseStrict |
Cyprus (cy)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| cyTic(opts?) | cyTics(count, opts?) | { value, digits, letter } | format, invalid, edge |
| cyPerson(opts?) | cyPeople(count, opts?) | { name, surname, initials, birthDate, tic } | sex, age/birth filters, invalid, edge, caseStrict |
Czechia (cz)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| czRodneCislo(opts?) | czRodneCislos(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, format, invalid, edge |
| czPerson(opts?) | czPeople(count, opts?) | { name, surname, initials, birthDate, rodneCislo } | sex, age/birth filters, invalid, edge, caseStrict |
| czIco(opts?) | czIcos(count, opts?) | { value, digits } | format, invalid, edge |
Denmark (dk)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| dkCpr(opts?) | dkCprs(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, checksum, format, invalid, edge |
| dkPerson(opts?) | dkPeople(count, opts?) | { name, surname, initials, birthDate, cpr } | same as dkCpr, plus caseStrict |
| dkCvr(opts?) | dkCvrs(count, opts?) | { value, digits } | format, invalid, edge |
Estonia (ee)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| eeIsikukood(opts?) | eeIsikukoods(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| eePerson(opts?) | eePeople(count, opts?) | { name, surname, initials, birthDate, isikukood } | sex, age/birth filters, invalid, edge, caseStrict |
| eeRegistrikood(opts?) | eeRegistrikoods(count, opts?) | { value, digits } | invalid, edge |
| eeKmkr(opts?) | eeKmkrs(count, opts?) | { value, digits } | format, invalid, edge |
Finland (fi)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| fiHenkilotunnus(opts?) | fiHenkilotunnuss(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| fiPerson(opts?) | fiPeople(count, opts?) | { name, surname, initials, birthDate, henkilotunnus } | sex, age/birth filters, invalid, edge, caseStrict |
| fiYTunnus(opts?) | fiYTunnuss(count, opts?) | { value, digits } | format, invalid, edge |
Germany (de)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| deSteuerId(opts?) | deSteuerIds(count, opts?) | { value, digits } | invalid, edge |
| dePerson(opts?) | dePeople(count, opts?) | { name, surname, initials, birthDate, steuerId } | sex, age/birth filters, invalid, edge, caseStrict |
| deUstIdnr(opts?) | deUstIdnrs(count, opts?) | { value, digits } | format, invalid, edge |
| deHandelsregisternummer(opts?) | deHandelsregisternummers(count, opts?) | { value, division, court, number } | division, edge |
| deWirtschaftsIdnr(opts?) | deWirtschaftsIdnrs(count, opts?) | { value, digits, suffix } | suffix, invalid, edge |
| dePersonalausweis(opts?) | dePersonalausweiss(count, opts?) | { value, serial, checkDigit } | invalid, edge |
Greece (gr)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| grAmka(opts?) | grAmkas(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| grPerson(opts?) | grPeople(count, opts?) | { name, surname, initials, birthDate, amka } | sex, age/birth filters, invalid, edge, caseStrict |
| grAfm(opts?) | grAfms(count, opts?) | { value, digits } | format, invalid, edge |
Hungary (hu)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| huAdoazonositoJel(opts?) | huAdoazonositoJels(count, opts?) | { value, digits, birthDate } | age/birth filters, invalid, edge |
| huTaj(opts?) | huTajs(count, opts?) | { value, digits } | invalid, edge |
| huSzemelyiAzonosito(opts?) | huSzemelyiAzonositos(count, opts?) | { value, digits, birthDate, sex, standard } | sex, age/birth filters, standard, invalid, edge |
| huPerson(opts?) | huPeople(count, opts?) | { name, surname, initials, birthDate, szemelyiAzonosito } | sex, age/birth filters, invalid, edge, caseStrict |
| huAdoszam(opts?) | huAdoszams(count, opts?) | { value, digits } | format, invalid, edge |
| huCegjegyzekszam(opts?) | huCegjegyzekszams(count, opts?) | { value, court, form, serial, digits } | edge |
Ireland (ie)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| iePpsn(opts?) | iePpsns(count, opts?) | { value, digits, checkLetter, secondLetter? } | standard, invalid, edge |
| iePerson(opts?) | iePeople(count, opts?) | { name, surname, initials, birthDate, ppsn } | sex, age/birth filters, invalid, edge, caseStrict |
| ieVat(opts?) | ieVats(count, opts?) | { value, digits } | format, standard, invalid, edge |
| ieCro(opts?) | ieCros(count, opts?) | { value, digits } | edge |
Italy (it)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| itCodiceFiscale(opts?) | itCodiceFiscales(count, opts?) | { value, surnameCode, nameCode, birthDate, sex } | sex, age/birth filters, surname, name, invalid, edge |
| itPerson(opts?) | itPeople(count, opts?) | { name, surname, initials, birthDate, codiceFiscale } | sex, age/birth filters, invalid, edge, caseStrict |
| itPartitaIva(opts?) | itPartitaIvas(count, opts?) | { value, digits } | format, invalid, edge |
Latvia (lv)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| lvPersonasKods(opts?) | lvPersonasKodss(count, opts?) | { value, digits, birthDate? } | age/birth filters, standard, format, invalid, edge |
| lvPerson(opts?) | lvPeople(count, opts?) | { name, surname, initials, birthDate, personasKods } | sex, age/birth filters, invalid, edge, caseStrict |
| lvRegistracijasNumurs(opts?) | lvRegistracijasNumurss(count, opts?) | { value, digits } | format, invalid, edge |
Lithuania (lt)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| ltAsmensKodas(opts?) | ltAsmensKodass(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| ltPerson(opts?) | ltPeople(count, opts?) | { name, surname, initials, birthDate, asmensKodas } | sex, age/birth filters, invalid, edge, caseStrict |
| ltImonesKodas(opts?) | ltImonesKodass(count, opts?) | { value, digits } | invalid, edge |
| ltPvm(opts?) | ltPvms(count, opts?) | { value, digits } | format, invalid, edge |
Luxembourg (lu)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| luMatricule(opts?) | luMatricules(count, opts?) | { value, digits, birthDate } | age/birth filters, invalid, edge |
| luPerson(opts?) | luPeople(count, opts?) | { name, surname, initials, birthDate, matricule } | sex, age/birth filters, invalid, edge, caseStrict |
| luTva(opts?) | luTvas(count, opts?) | { value, digits } | format, invalid, edge |
Malta (mt)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| mtIdCard(opts?) | mtIdCards(count, opts?) | { value, digits, category } | category, edge |
| mtPerson(opts?) | mtPeople(count, opts?) | { name, surname, initials, birthDate, idCard } | sex, age/birth filters, invalid, edge, caseStrict |
| mtVat(opts?) | mtVats(count, opts?) | { value, digits } | format, invalid, edge |
Netherlands (nl)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| nlBsn(opts?) | nlBsns(count, opts?) | { value, digits } | invalid, edge |
| nlPerson(opts?) | nlPeople(count, opts?) | { name, surname, initials, birthDate, bsn } | sex, age/birth filters, invalid, edge, caseStrict |
| nlRsin(opts?) | nlRsins(count, opts?) | { value, digits } | invalid, edge |
| nlBtwId(opts?) | nlBtwIds(count, opts?) | { value, digits, standard } | standard, invalid, edge |
| nlKvk(opts?) | nlKvks(count, opts?) | { value, digits } | edge |
Portugal (pt)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| ptNif(opts?) | ptNifs(count, opts?) | { value, digits, entity } | entity, format, invalid, edge |
| ptCartaoCidadao(opts?) | ptCartaoCidadaos(count, opts?) | { value, nic, version } | invalid, edge |
| ptPerson(opts?) | ptPeople(count, opts?) | { name, surname, initials, birthDate, cartaoCidadao } | sex, age/birth filters, invalid, edge, caseStrict |
Romania (ro)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| roCnp(opts?) | roCnps(count, opts?) | { value, digits, birthDate, sex, county } | sex, age/birth filters, invalid, edge |
| roPerson(opts?) | roPeople(count, opts?) | { name, surname, initials, birthDate, cnp } | sex, age/birth filters, invalid, edge, caseStrict |
| roCui(opts?) | roCuis(count, opts?) | { value, digits } | format, invalid, edge |
Slovakia (sk)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| skRodneCislo(opts?) | skRodneCislos(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, format, invalid, edge |
| skPerson(opts?) | skPeople(count, opts?) | { name, surname, initials, birthDate, rodneCislo } | sex, age/birth filters, invalid, edge, caseStrict |
| skIco(opts?) | skIcos(count, opts?) | { value, digits } | invalid, edge |
| skIcDph(opts?) | skIcDphs(count, opts?) | { value, digits } | format, invalid, edge |
Slovenia (si)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| siEmso(opts?) | siEmsos(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, invalid, edge |
| siPerson(opts?) | siPeople(count, opts?) | { name, surname, initials, birthDate, emso } | sex, age/birth filters, invalid, edge, caseStrict |
| siDavcnaStevilka(opts?) | siDavcnaStevilkas(count, opts?) | { value, digits } | format, invalid, edge |
Spain (es)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| esDni(opts?) | esDnis(count, opts?) | { value, digits, letter } | invalid, edge |
| esPerson(opts?) | esPeople(count, opts?) | { name, surname, initials, birthDate, dni } | sex, age/birth filters, invalid, edge, caseStrict |
| esNie(opts?) | esNies(count, opts?) | { value, prefix, digits, letter } | invalid, edge |
| esCif(opts?) | esCifs(count, opts?) | { value, digits } | format, invalid, edge |
Sweden (se)
| Singular | Plural | Returns (singular) | Options |
| -------- | ------ | ------------------ | ------- |
| sePersonnummer(opts?) | sePersonnummers(count, opts?) | { value, digits, birthDate, sex } | sex, age/birth filters, format, kind, invalid, edge |
| sePerson(opts?) | sePeople(count, opts?) | { name, surname, initials, birthDate, personnummer } | sex, age/birth filters, invalid, edge, caseStrict |
| seOrganisationsnummer(opts?) | seOrganisationsnummers(count, opts?) | { value, digits } | format, invalid, edge |
Vehicle registration plates
Every EU member state exposes <cc>VehicleRegistration(opts?) / <cc>VehicleRegistrations(count, opts?), returning at least { value, type } (plus country-specific fields — region/district/province codes, era, expiryMonth, subregion, …). Each country owns its own type union — the plate kinds that country actually issues — so the returned type and the accepted type option are typed per country. Every method takes the shared edge and extreme triggers; other options (format, era, region filters) vary by country as listed below.
| Country | Method prefix | type values | Other options |
| ------- | ------------- | ------------- | ------------- |
| Poland (pl) | plVehicleRegistration | standard, custom, police, military, historic, motorcycle, short | voivodeship, county, format |
| Austria (at) | atVehicleRegistration | standard, custom, motorcycle, military, police, diplomatic | district, state, format |
| Belgium (be) | beVehicleRegistration | standard, custom, motorcycle, moped, diplomatic, dealer, export, oldtimer, taxi, trailer | format |
| Bulgaria (bg) | bgVehicleRegistration | standard, custom, motorcycle, military, diplomatic, electric, temporary | script, province, format |
| Cyprus (cy) | cyVehicleRegistration | standard, motorcycle, diplomatic, trailer, un, sba | format |
| Czechia (cz) | czVehicleRegistration | standard, extended, custom, motorcycle, diplomatic, historic, electric, military | region, format |
| Germany (de) | deVehicleRegistration | standard, custom, seasonal, historic, electric, motorcycle, military, diplomatic | district, era, format |
| Denmark (dk) | dkVehicleRegistration | standard, custom, diplomatic, trailer, motorcycle, export | era, format |
| Estonia (ee) | eeVehicleRegistration | standard, custom, diplomatic, dealer, racing, classic, motorcycle, moped, transit, military | era, format |
| Spain (es) | esVehicleRegistration | standard, motorcycle, military, diplomatic, guardia-civil, national-police, temporary, dealer, historic, trailer | era, province, format |
| Finland (fi) | fiVehicleRegistration | standard, custom, diplomatic, export, dealer, museum, trailer, military, motorcycle, aland | era, format |
| France (fr) | frVehicleRegistration | standard, diplomatic, temporary, export, motorcycle | era, withDepartment, department, format |
| Greece (gr) | grVehicleRegistration | standard, motorcycle, taxi, diplomatic, historic, trailer | script, era, region, format |
| Croatia (hr) | hrVehicleRegistration | standard, custom, military, diplomatic, export, historic, motorcycle | city, format |
| Hungary (hu) | huVehicleRegistration | standard, custom, military, diplomatic, police, oldtimer, taxi, electric, motorcycle, temporary | era, format |
| Ireland (ie) | ieVehicleRegistration | standard, temporary-import, vintage, taxi, electric | era, county, format |
| Italy (it) | itVehicleRegistration | standard, motorcycle, military, diplomatic, trailer | withProvince, province, format |
| Lithuania (lt) | ltVehicleRegistration | standard, custom, military, diplomatic, motorcycle, moped, trailer, ev, taxi, historic, dealer | format |
| Luxembourg (lu) | luVehicleRegistration | standard, custom, military, diplomatic, official, dealer, export, moped, deputies | era, format |
| Latvia (lv) | lvVehicleRegistration | standard, custom, military, diplomatic, consular, historic, taxi, transit, motorcycle, moped | format |
| Malta (mt) | mtVehicleRegistration | standard, motorcycle, military, government, diplomatic, taxi, bus, trailer, tax-free, custom | expiryMonth, format |
| Netherlands (nl) | nlVehicleRegistration | standard, motorcycle, historic, diplomatic, military, dealer, export | sidecode, era, format |
| Portugal (pt) | ptVehicleRegistration | standard, motorcycle, military, diplomatic, police, export | era, format |
| Romania (ro) | roVehicleRegistration | standard, motorcycle, electric, temporary, military, police, diplomatic | county, format |
| Sweden (se) | seVehicleRegistration | standard, custom, diplomatic, military, motorcycle | era, format |
| Slovenia (si) | siVehicleRegistration | standard, custom, motorcycle, military, diplomatic, police, export | region, format |
| Slovakia (sk) | skVehicleRegistration | standard, motorcycle, military, diplomatic, consular, trailer, historic, electric, dealer, custom | era, district, region, format |
All 29 at once — vehicleRegistration(opts?) / vehicleRegistrations(count, opts?) draw a country per record and return { country, value, type, category, region? }. Because the 29 type unions above share no vocabulary — standard in Italy is passenger in the US, oldtimer in Hungary is vintage in Ireland is museum in Finland — the cross-country type takes a shared category instead: standard, custom, motorcycle, moped, military, police, diplomatic, government, commercial, taxi, trailer, historic, electric, temporary, export, dealer, other. Each record reports both — category is what you asked for, type is what that country calls it.
A type narrows the pool to the countries that issue it ({ type: 'taxi' } draws from the seven that do), and combining it with a countries pool that issues nothing of the kind is a 400 rather than a silent substitution. format, era, script and the region filters stay on the per-country methods, where their vocabularies are exact.
const plates = await fakeData.vehicleRegistrations(3, { type: 'historic' });
// [{ country: 'ie', value: 'ZV 4861', type: 'vintage', category: 'historic' },
// { country: 'hr', value: 'GN-PV-842', type: 'historic', category: 'historic' },
// { country: 'pl', value: 'SRB 5S', type: 'historic', category: 'historic', region: 'śląskie' }]IBANs
Every EU member state exposes <cc>Iban(opts?) / <cc>Ibans(count, opts?), all returning the same shape { value, electronicFormat, bankCode, bankName }. The IBAN carries correct mod-97 check digits and, where the national account number has an internal check digit, that is reproduced too. Pin the issuing bank with bankCode (country-specific width) or bankName (case-insensitive fragment, mutually exclusive), or let it be chosen at random. Every method takes format (grouped/compact) and the shared invalid / edge / extreme triggers.
plIban, deIban, atIban, beIban, bgIban, cyIban, czIban, dkIban, eeIban, esIban, fiIban, frIban, grIban, hrIban, huIban, ieIban, itIban, ltIban, luIban, lvIban, mtIban, nlIban, ptIban, roIban, seIban, siIban, skIban — each hits GET /v1/<cc>/iban.
Locale-agnostic
| Singular | Plural | Returns (singular) | Common options |
| ------------------------------ | -------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------- |
| lorem(opts?) | lorems(count, opts?) | { value, words, chars, bytes, paragraphs, startedWithLorem } | bytes, chars, words, paragraphs, startWithLorem |
| customRegex(opts) | customRegexes(count, opts) | { value, pattern } | pattern (required), maxRepetition — needs a free account |
| enum(opts) | enums(count, opts) | { value, probability } | choices (weighted map {member:weight} or bare array […] for equal weights) or dictionary (name of an enum Dictionary defined in the Real-Fake-Data.com dashboard; needs the team's API key in headers) — exactly one; edge, extreme, invalid |
| object(opts) | objects(count, opts) | { value, probability } | choices (weighted {object,weight}[] or bare array of objects for equal weights) or dictionary (name of an object Dictionary defined in the Real-Fake-Data.com dashboard; needs the team's API key in headers) — exactly one; edge, extreme, invalid |
Synthetic IDs
The technical identifiers a record needs but no national registry issues — primary keys. Locale-agnostic, no prefix. Deterministic from the seed, so a seeded UUID is reproducible; any embedded timestamp (UUID v7, ULID, ObjectId) is seed-derived, not wall-clock.
| Singular | Plural | Returns (singular) | Common options |
| ------------------------------ | -------------------------------------- | --------------------------- | --------------------------------------- |
| uuid(opts?) | uuids(count, opts?) | { value, version } | version: '4' \| '7' (default '4') |
| ulid(opts?) | ulids(count, opts?) | { value } | — |
| nanoId(opts?) | nanoIds(count, opts?) | { value } | size (default 21), alphabet |
| objectId(opts?) | objectIds(count, opts?) | { value } | — |
| sequence(opts?) | sequences(count, opts?) | { value } | start (default 1), step (default 1) |
For sequence, a plural call returns the run start, start + step, …; a singular call returns start.
Generating many records at once
Every plural takes count as its first argument and returns an array. The bound (10 by default, raised once you have credits) is enforced by the API — an out-of-range count throws a RealFakeDataError (HTTP 400), never a silent clamp. A plural is a single request, so — when a seed is in play — it consumes one slot of the seed sequence just like a singular call.
const team = await fakeData.plPeople(5, { sex: 'f' }); // PolishPersonData[] of length 5
const inboxes = await fakeData.emails(3); // EmailData[] of length 3Special triggers
Three opt-in flags steer generators toward the cases real-world data throws at your code:
invalid: true— on every checksum generator (plPesel,plNip,plRegon,plIban,plCompany,plIdCard,plPassport,plLandRegister), produces a value with a deliberately wrong check digit while the rest stays well-formed. For asserting that your own validators reject bad input. When set, the responsemeta.invalidistrueso a batch can tell the broken record apart.edge: true— on the name generators (<cc>PersonName,<cc>CompanyName,personName,companyName,plCompany), biases toward edge-case shapes: second given names, double-barrelled surnames, very short names, punctuation-heavy or unusually long company names. The corners that break naïve form validation and layout.caseStrict: false— on the person-name generators, deliberately mangles the casing of name and surname (all-lower, all-upper, or random); initials stay proper uppercase. Defaults totrue(proper casing). For testing case-insensitive matching and normalisation.
const bad = await fakeData.plNip({ invalid: true });
await expect(submitNip(bad.value)).rejects.toThrow('invalid checksum');
const messy = await fakeData.dePersonName({ caseStrict: false }); // e.g. { name: 'hANS', surname: 'müller' }
const tricky = await fakeData.itCompanyName({ edge: true }); // punctuation-heavy / long-form nameError handling
A non-2xx API response throws a RealFakeDataError carrying status (HTTP code), code (the API's machine error code), and details (per-field validation messages):
import { RealFakeDataError } from '@przeslijmi/real-fake-data-playwright';
try {
await fakeData.plAddress({ teryt: 'not-digits' });
} catch (error) {
if (error instanceof RealFakeDataError) {
console.log(error.status, error.code, error.details);
// 400 'VALIDATION_ERROR' [{ path: 'teryt', message: 'teryt must be 1–7 digits' }]
}
}Advanced: using the client without the fixture
The fixture is a thin wrapper over a provider and a facade you can use directly — handy in global setup, scripts, or non-Playwright code:
import { CloudFakeDataProvider, createFakeData } from '@przeslijmi/real-fake-data-playwright';
const provider = new CloudFakeDataProvider({ baseUrl: 'https://api.real-fake-data.com' });
const fakeData = createFakeData(provider, { seed: 42 });
const company = await fakeData.plCompanyName({ legalForm: 'S.A.' });The FakeDataProvider interface is the swap point: the cloud provider talks HTTP to the hosted API, and the same facade can run against other backends.
License
MIT
This repository is auto-generated from a private upstream monorepo. Open issues here, but code changes are made upstream and re-synced — pull requests against this repo are applied upstream, not merged directly.
