From e28e6b6a87e5cb36b3fc3a002a9b4b91bab01052 Mon Sep 17 00:00:00 2001 From: Adriano Pontes Date: Fri, 30 Oct 2026 17:01:48 -0300 Subject: [PATCH 1/2] feat: add CNH validator and input formatter --- README.md | 4 +- example/lib/main.dart | 3 + example/test/example_page_test.dart | 34 ++++ lib/brasil_fields.dart | 1 + lib/src/formatters/cnh_input_formatter.dart | 23 +++ lib/src/util/util_brasil_fields.dart | 7 + lib/src/validators/cnh_validator.dart | 127 ++++++++++++++ lib/src/validators/validators.dart | 1 + test/cnh_test.dart | 159 ++++++++++++++++++ test/helpers/deterministic_random.dart | 32 ++++ test/public_api_test.dart | 1 + .../formatters/cnh_input_formatter_test.dart | 101 +++++++++++ test/util_brasil_fields_test.dart | 18 ++ 13 files changed, 510 insertions(+), 1 deletion(-) create mode 100644 lib/src/formatters/cnh_input_formatter.dart create mode 100644 lib/src/validators/cnh_validator.dart create mode 100644 test/cnh_test.dart create mode 100644 test/src/formatters/cnh_input_formatter_test.dart diff --git a/README.md b/README.md index c7a32ff..9f3dcd6 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ final cnpjField = TextFormField( | CPF ou CNPJ | CpfOuCnpjFormatter() | Máscara numérica conforme o tamanho | | CPF ou CNPJ alfanumérico | CpfOuCnpjAlfanumericoFormatter() | Máscara conforme o tamanho | | CEST | CESTInputFormatter() | 12.345.67 | +| CNH | CNHInputFormatter() | 12345678901 (11 dígitos, sem máscara) | | CNS | CNSInputFormatter() | 111 2222 3333 4444 | | Data | DataInputFormatter() | 01/01/1900 | | Hora | HoraInputFormatter() | 23:59 | @@ -128,6 +129,7 @@ Formata objetos `DateTime` e interpreta strings de data e hora no padrão brasil Gera, formata e valida identificadores e valores brasileiros: +- `UtilBrasilFields.gerarCNH()` (XXXXXXXXXXX) - `UtilBrasilFields.gerarCPF()` (XXXXXXXXXXX) - `UtilBrasilFields.gerarCPF(useFormat: true)` (XXX.XXX.XXX-XX) - `UtilBrasilFields.gerarCNPJ()` (XXXXXXXXXXXXXX) @@ -155,7 +157,7 @@ Gera, formata e valida identificadores e valores brasileiros: - `UtilBrasilFields.removeCaracteres` mantém apenas letras ASCII e dígitos. - `UtilBrasilFields.removerSimboloMoeda` remove `R$` e o espaço seguinte, se houver. - `UtilBrasilFields.converterMoedaParaDouble` converte um valor monetário brasileiro em `double`; retorna `0` se a entrada não vazia não puder ser convertida. -- `UtilBrasilFields.isCPFValido`, `isCNPJValido`, `isNUPValido` e `isPisPasepValido` retornam `bool`. Para CNPJ alfanumérico, passe `isAlphanumeric: true` a `isCNPJValido`. +- `UtilBrasilFields.isCNHValido`, `isCPFValido`, `isCNPJValido`, `isNUPValido` e `isPisPasepValido` retornam `bool`. Para CNPJ alfanumérico, passe `isAlphanumeric: true` a `isCNPJValido`. Os métodos `obterCpf`, `obterCnpj`, `obterNUP` e `obterPisPasep` exigem identificadores válidos e lançam `ArgumentError` para valores inválidos. diff --git a/example/lib/main.dart b/example/lib/main.dart index 10c876c..9cb8583 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -887,6 +887,7 @@ class _FormatterDemo { return 'CnpjAlfanumericoInputFormatter'; } if (value is CnpjInputFormatter) return 'CnpjInputFormatter'; + if (value is CNHInputFormatter) return 'CNHInputFormatter'; if (value is CNSInputFormatter) return 'CNSInputFormatter'; if (value is CpfOuCnpjAlfanumericoFormatter) { return 'CpfOuCnpjAlfanumericoFormatter'; @@ -956,6 +957,8 @@ List<_FormatterDemo> _createFormatterDemos() => [ CertNascimentoInputFormatter()), _demo('nup', 'Documentos e identificadores', 'NUP', '0601064-21.2022.6.00.0000', NUPInputFormatter()), + _demo('cnh', 'Documentos e identificadores', 'CNH', '18718682003', + CNHInputFormatter()), _demo('cns', 'Documentos e identificadores', 'CNS', '000 1111 2222 3333', CNSInputFormatter()), _demo('telefone-fixo', 'Novidades recentes', 'Telefone fixo', diff --git a/example/test/example_page_test.dart b/example/test/example_page_test.dart index a4e63d0..63905e4 100644 --- a/example/test/example_page_test.dart +++ b/example/test/example_page_test.dart @@ -148,6 +148,40 @@ void main() { expect(editable.controller.text, '120.12345.67-2'); }); + testWidgets('shows and limits the CNH formatter', (tester) async { + await tester.pumpWidget(const BrasilFieldsApp()); + await tester.pumpAndSettle(); + + await tester.enterText( + find.byKey(const Key('formatter-search')), + 'CNH', + ); + await tester.pumpAndSettle(); + await tester.drag( + find.byKey(const PageStorageKey('formatters-page')), + const Offset(0, -500), + ); + await tester.pumpAndSettle(); + + final field = find.byKey(const Key('formatter-cnh')); + expect(field, findsOneWidget); + expect(find.text('CNHInputFormatter'), findsOneWidget); + + await tester.enterText(field, '18718682003'); + await tester.pump(); + + final editable = tester.widget( + find.descendant(of: field, matching: find.byType(EditableText)), + ); + expect(editable.controller.text, '18718682003'); + + // O décimo segundo dígito é recusado e o valor anterior é mantido. + await tester.enterText(field, '187186820033'); + await tester.pump(); + + expect(editable.controller.text, '18718682003'); + }); + testWidgets('narrow layout renders without overflow', (tester) async { tester.view.physicalSize = const Size(320, 568); tester.view.devicePixelRatio = 1; diff --git a/lib/brasil_fields.dart b/lib/brasil_fields.dart index 63f4ac5..c1b5925 100644 --- a/lib/brasil_fields.dart +++ b/lib/brasil_fields.dart @@ -7,6 +7,7 @@ export 'src/formatters/centavos_input_formatter.dart'; export 'src/formatters/cep_input_formatter.dart'; export 'src/formatters/cert_nascimento_input_formatter.dart'; export 'src/formatters/cest_input_formatter.dart'; +export 'src/formatters/cnh_input_formatter.dart'; export 'src/formatters/cnpj_input_formatter.dart'; export 'src/formatters/cnpj_alfanumerico_input_formatter.dart'; export 'src/formatters/cns_formatter.dart'; diff --git a/lib/src/formatters/cnh_input_formatter.dart b/lib/src/formatters/cnh_input_formatter.dart new file mode 100644 index 0000000..4f1d9f3 --- /dev/null +++ b/lib/src/formatters/cnh_input_formatter.dart @@ -0,0 +1,23 @@ +import 'package:flutter/services.dart'; + +/// Limita o campo aos 11 dígitos do número de registro da CNH. +/// +/// O número de registro não usa separadores, portanto o texto é mantido como +/// digitado, incluindo a seleção: o formatador apenas impede que o campo +/// passe de [maxLength]. +class CNHInputFormatter extends TextInputFormatter { + /// Número máximo de caracteres aceitos pelo formatador. + int get maxLength => 11; + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + if (!newValue.composing.isCollapsed) return newValue; + + if (newValue.text.length > maxLength) return oldValue; + + return newValue; + } +} diff --git a/lib/src/util/util_brasil_fields.dart b/lib/src/util/util_brasil_fields.dart index 2a2f16a..9722981 100644 --- a/lib/src/util/util_brasil_fields.dart +++ b/lib/src/util/util_brasil_fields.dart @@ -117,6 +117,9 @@ class UtilBrasilFields { return telefone.substring(1, 3); } + /// Retorna `true` se o número de registro da CNH for válido. + static bool isCNHValido(String? cnh) => CNHValidator.isValid(cnh); + /// Retorna `true` se o CPF for válido. static bool isCPFValido(String? cpf) => CPFValidator.isValid(cpf); @@ -134,6 +137,10 @@ class UtilBrasilFields { /// Retorna `true` se o PIS/PASEP (NIT/NIS) for válido. static bool isPisPasepValido(String? pis) => PisPasepValidator.isValid(pis); + /// Gera um número de registro de CNH válido aleatório, com 11 dígitos. + static String gerarCNH({Random? random}) => + CNHValidator.generate(random: random); + /// Gera um CPF válido aleatório. /// /// Com [useFormat] igual a `true`, retorna `XXX.XXX.XXX-XX`; diff --git a/lib/src/validators/cnh_validator.dart b/lib/src/validators/cnh_validator.dart new file mode 100644 index 0000000..fac928b --- /dev/null +++ b/lib/src/validators/cnh_validator.dart @@ -0,0 +1,127 @@ +import 'dart:math'; + +/// Valida e gera números de registro de CNH. +/// +/// O número de registro tem 11 dígitos, sem máscara, sendo os dois últimos +/// os dígitos verificadores. +class CNHValidator { + /// Sequências de dígitos repetidos são consideradas inválidas. + static const List blockList = [ + '00000000000', + '11111111111', + '22222222222', + '33333333333', + '44444444444', + '55555555555', + '66666666666', + '77777777777', + '88888888888', + '99999999999', + ]; + + static const stripRegex = r'[^\d]'; + static const int _maxGenerationAttempts = 100; + + // Calcula os dois dígitos verificadores pelo módulo 11, com pesos de 9 a 1 + // no primeiro dígito e de 1 a 9 no segundo. Quando o primeiro resto é 10, o + // primeiro dígito é 0 e o segundo é reduzido em 2. + // + // Retorna `null` para as bases em que o segundo dígito não cabe em uma casa + // decimal, ou seja, que não admitem número de registro válido. + static String? _verifierDigits(String base) { + var somaPrimeiro = 0; + var somaSegundo = 0; + + for (var i = 0; i < 9; i++) { + final digito = int.parse(base[i], radix: 10); + somaPrimeiro += digito * (9 - i); + somaSegundo += digito * (i + 1); + } + + var primeiro = somaPrimeiro % 11; + final reducao = primeiro >= 10 ? 2 : 0; + if (primeiro >= 10) { + primeiro = 0; + } + + var segundo = somaSegundo % 11; + if (segundo >= 10) { + segundo = 0; + } else { + segundo -= reducao; + if (segundo < 0) { + segundo += 11; + } + } + + if (segundo > 9) { + return null; + } + + return '$primeiro$segundo'; + } + + /// Remove caracteres não numéricos da CNH. + static String strip(String? cnh) { + final regExp = RegExp(stripRegex); + cnh = cnh ?? ''; + + return cnh.replaceAll(regExp, ''); + } + + /// Retorna `true` se [cnh] tem formato e dígitos verificadores válidos. + /// + /// Remove caracteres não numéricos antes da validação quando + /// [stripBeforeValidation] é `true`. + static bool isValid(String? cnh, {bool stripBeforeValidation = true}) { + if (stripBeforeValidation) { + cnh = strip(cnh); + } + + if (cnh == null || cnh.isEmpty) { + return false; + } + + if (cnh.length != 11) { + return false; + } + + if (!RegExp(r'^\d{11}$').hasMatch(cnh)) { + return false; + } + + if (blockList.contains(cnh)) { + return false; + } + + return _verifierDigits(cnh.substring(0, 9)) == cnh.substring(9); + } + + /// Gera um número de registro de CNH válido, com 11 dígitos. + static String generate({Random? random}) { + final generator = random ?? Random(); + + for (var attempt = 0; attempt < _maxGenerationAttempts; attempt++) { + var base = ''; + + for (var i = 0; i < 9; i += 1) { + base += generator.nextInt(10).toString(); + } + + final digits = _verifierDigits(base); + if (digits == null) { + continue; + } + + final candidate = '$base$digits'; + if (!blockList.contains(candidate)) { + return candidate; + } + } + + throw StateError( + 'Não foi possível gerar uma CNH válida após ' + '$_maxGenerationAttempts tentativas.', + ); + } +} diff --git a/lib/src/validators/validators.dart b/lib/src/validators/validators.dart index 463574a..85bdc90 100644 --- a/lib/src/validators/validators.dart +++ b/lib/src/validators/validators.dart @@ -1,3 +1,4 @@ +export 'cnh_validator.dart'; export 'cnpj_alfanumerico_validator.dart'; export 'cnpj_validator.dart'; export 'cpf_validator.dart'; diff --git a/test/cnh_test.dart b/test/cnh_test.dart new file mode 100644 index 0000000..f74bb65 --- /dev/null +++ b/test/cnh_test.dart @@ -0,0 +1,159 @@ +import 'dart:math'; + +import 'package:brasil_fields/brasil_fields.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'helpers/deterministic_random.dart'; + +void main() { + test('Test CNH validator', () { + expect(CNHValidator.isValid('18718682003'), true); + expect(CNHValidator.isValid('40673094272'), true); + expect(CNHValidator.isValid('97028218846'), true); + expect(CNHValidator.isValid('56220269750'), true); + + // Dígitos verificadores incorretos. + expect(CNHValidator.isValid('18718682004'), false); + expect(CNHValidator.isValid('18718682013'), false); + + // Tamanho inválido. + expect(CNHValidator.isValid('1871868200'), false); + expect(CNHValidator.isValid('187186820033'), false); + + // Valores vazios ou nulos. + expect(CNHValidator.isValid(null), false); + expect(CNHValidator.isValid(''), false); + + // Dígitos repetidos. + expect(CNHValidator.isValid('00000000000'), false); + expect(CNHValidator.isValid('11111111111'), false); + expect(CNHValidator.isValid('99999999999'), false); + + // Validação sem remover os separadores. + expect( + CNHValidator.isValid('187.186.820-03', stripBeforeValidation: false), + false, + ); + expect( + CNHValidator.isValid('18718682003', stripBeforeValidation: false), + true, + ); + }); + + test('CNH validator rejects malformed raw values of the exact length', () { + const malformed = [ + '1871868200A', + 'A8718682003', + '187186820-3', + '187.18682003', + ]; + + for (final cnh in malformed) { + expect( + CNHValidator.isValid(cnh, stripBeforeValidation: false), + isFalse, + reason: cnh, + ); + } + }); + + test('CNH validator rejects every repeated-digit sequence', () { + for (var digit = 0; digit <= 9; digit++) { + final repeated = digit.toString() * 11; + + // A lista de bloqueio é necessária: as sequências repetidas satisfazem + // o cálculo dos dígitos verificadores. + expect(cnhWithIndependentCheckDigits(digit.toString() * 9), repeated); + expect(CNHValidator.blockList, contains(repeated)); + expect(CNHValidator.isValid(repeated), isFalse, reason: repeated); + } + }); + + test('Test CNH strip', () { + expect(CNHValidator.strip('187.186.820-03'), '18718682003'); + expect(CNHValidator.strip(null), ''); + }); + + test('CNH generator produces a deterministic valid registration', () { + final random = RecordingRandom([0, 1, 2, 3, 4, 5, 6, 7, 8]); + final generated = CNHValidator.generate(random: random); + + expect(generated, cnhWithIndependentCheckDigits('012345678')); + expect(generated, '01234567807'); + expect(generated, matches(RegExp(r'^\d{11}$'))); + expect(CNHValidator.isValid(generated), isTrue); + expect(random.requestedMaxValues, hasLength(9)); + expect(random.requestedMaxValues, everyElement(10)); + }); + + test('CNH generator skips a blocklisted candidate', () { + final random = RecordingRandom([ + ...List.filled(9, 0), + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ]); + final generated = CNHValidator.generate(random: random); + final blocked = cnhWithIndependentCheckDigits('000000000'); + + expect(blocked, '00000000000'); + expect(CNHValidator.blockList, contains(blocked)); + expect(generated, '01234567807'); + expect(CNHValidator.isValid(generated), isTrue); + expect(random.requestedMaxValues, hasLength(18)); + expect(random.requestedMaxValues, everyElement(10)); + }); + + test('CNH generator skips bases without valid check digits', () { + // A base 225711825 deixa o primeiro resto em 10 e o segundo em 1, o que + // não produz um segundo dígito de uma casa. + expect(cnhWithIndependentCheckDigits('225711825'), isNull); + + final random = RecordingRandom([ + 2, + 2, + 5, + 7, + 1, + 1, + 8, + 2, + 5, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ]); + final generated = CNHValidator.generate(random: random); + + expect(generated, '01234567807'); + expect(CNHValidator.isValid(generated), isTrue); + expect(random.requestedMaxValues, hasLength(18)); + }); + + test('CNH generator agrees with an independent check-digit calculation', () { + final random = Random(7); + + for (var attempt = 0; attempt < 50; attempt++) { + final generated = CNHValidator.generate(random: random); + + expect( + generated, + cnhWithIndependentCheckDigits(generated.substring(0, 9)), + reason: generated, + ); + expect(CNHValidator.isValid(generated), isTrue, reason: generated); + } + }); +} diff --git a/test/helpers/deterministic_random.dart b/test/helpers/deterministic_random.dart index db941e2..7d039cf 100644 --- a/test/helpers/deterministic_random.dart +++ b/test/helpers/deterministic_random.dart @@ -33,6 +33,38 @@ class RecordingRandom implements Random { } } +/// Calcula os dígitos verificadores da CNH a partir de uma base de 9 dígitos. +/// +/// Retorna `null` para bases que não admitem número de registro válido. +String? cnhWithIndependentCheckDigits(String body) { + const firstWeights = [9, 8, 7, 6, 5, 4, 3, 2, 1]; + const secondWeights = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + + var firstSum = 0; + var secondSum = 0; + for (var index = 0; index < body.length; index++) { + final value = int.parse(body[index]); + firstSum += value * firstWeights[index]; + secondSum += value * secondWeights[index]; + } + + final firstRemainder = firstSum % 11; + final first = firstRemainder > 9 ? 0 : firstRemainder; + final reduction = firstRemainder > 9 ? 2 : 0; + + final secondRemainder = secondSum % 11; + var second = secondRemainder > 9 ? 0 : secondRemainder - reduction; + if (second < 0) { + second += 11; + } + + if (second > 9) { + return null; + } + + return '$body$first$second'; +} + String cpfWithIndependentCheckDigits(String body) { int digit(String value) { var sum = 0; diff --git a/test/public_api_test.dart b/test/public_api_test.dart index bce5d1e..85bf8b5 100644 --- a/test/public_api_test.dart +++ b/test/public_api_test.dart @@ -12,6 +12,7 @@ void main() { }); test('public entrypoint exposes every validator', () { + expect(CNHValidator.isValid('18718682003'), isTrue); expect(CPFValidator.isValid('334.616.710-02'), isTrue); expect(CNPJValidator.isValid('12.175.094/0001-19'), isTrue); expect( diff --git a/test/src/formatters/cnh_input_formatter_test.dart b/test/src/formatters/cnh_input_formatter_test.dart new file mode 100644 index 0000000..2103427 --- /dev/null +++ b/test/src/formatters/cnh_input_formatter_test.dart @@ -0,0 +1,101 @@ +import 'package:brasil_fields/brasil_fields.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'formatter_test_harness.dart'; + +void main() { + final formatters = numericFormatterChain(CNHInputFormatter()); + TextEditingValue evaluate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) => + applyFormatterChain(formatters, oldValue, newValue); + + group('CNHInputFormatter', () { + test('mantém os 11 dígitos sem separadores', () { + final actual = evaluate( + textEditingValue(''), + textEditingValue('18718682003'), + ); + + expect(actual.text, '18718682003'); + expectCollapsedSelectionAtEnd(actual); + expect(actual.composing, TextRange.empty); + }); + + test('a cadeia numérica descarta caracteres não numéricos', () { + final actual = evaluate( + textEditingValue(''), + textEditingValue('187.186.820-03'), + ); + + expect(actual.text, '18718682003'); + }); + + test('digitação sequencial preserva o cursor no fim', () { + var state = textEditingValue(''); + const raw = '18718682003'; + + for (var length = 1; length <= raw.length; length++) { + state = evaluate(state, textEditingValue(raw.substring(0, length))); + expect(state.text, raw.substring(0, length)); + expectCollapsedSelectionAtEnd(state); + } + }); + + test('backspace remove um dígito por vez', () { + var state = textEditingValue('18718682003'); + const cases = [ + '1871868200', + '187186820', + '18718682', + '1871868', + '187', + '18', + '1', + '', + ]; + + for (final expected in cases) { + state = evaluate(state, textEditingValue(expected)); + expect(state.text, expected); + expectCollapsedSelectionAtEnd(state); + } + }); + + test('edição no meio preserva a posição do cursor', () { + final actual = evaluate( + textEditingValue('18718682003'), + textEditingValue( + '18918682003', + selection: const TextSelection.collapsed(offset: 3), + ), + ); + + expect(actual.text, '18918682003'); + expect(actual.selection, const TextSelection.collapsed(offset: 3)); + }); + + test('limite de 11 dígitos preserva valor e seleção anteriores', () { + final oldValue = textEditingValue( + '18718682003', + selection: const TextSelection(baseOffset: 3, extentOffset: 8), + ); + + expect(evaluate(oldValue, textEditingValue('187186820033')), oldValue); + }); + + test('composição ativa permanece inalterada', () { + final value = textEditingValue( + '1871', + composing: const TextRange(start: 0, end: 4), + ); + + expectActiveComposingIsUnchanged( + formatters, + textEditingValue('187'), + value, + ); + }); + }); +} diff --git a/test/util_brasil_fields_test.dart b/test/util_brasil_fields_test.dart index 35360ed..871d1b8 100644 --- a/test/util_brasil_fields_test.dart +++ b/test/util_brasil_fields_test.dart @@ -315,6 +315,12 @@ void main() { }); group('Validadores públicos', () { + test('CNH cobre valores válidos, inválidos e nulos', () { + expect(UtilBrasilFields.isCNHValido('18718682003'), isTrue); + expect(UtilBrasilFields.isCNHValido('18718682004'), isFalse); + expect(UtilBrasilFields.isCNHValido(null), isFalse); + }); + test('CPF cobre valores válidos, inválidos e nulos', () { expect(UtilBrasilFields.isCPFValido('334.616.710-02'), isTrue); expect(UtilBrasilFields.isCPFValido('334.616.710-01'), isFalse); @@ -499,6 +505,18 @@ void main() { }); group('Geradores públicos', () { + test('CNH aceita Random injetado e retorna apenas dígitos', () { + final defaultCnh = UtilBrasilFields.gerarCNH(); + final generated = UtilBrasilFields.gerarCNH( + random: RecordingRandom([9, 0, 1, 2, 3, 4, 5, 6, 7]), + ); + + expect(defaultCnh, matches(RegExp(r'^\d{11}$'))); + expect(generated, cnhWithIndependentCheckDigits('901234567')); + expect(generated, '90123456707'); + expect(UtilBrasilFields.isCNHValido(generated), isTrue); + }); + test('exemplos de CPF usam parâmetros nomeados e defaults crus', () { final defaultCpf = UtilBrasilFields.gerarCPF(); final explicitRaw = UtilBrasilFields.gerarCPF( From 629d15b77abede060c27acc86279a3a48f96bada Mon Sep 17 00:00:00 2001 From: Adriano Pontes Date: Fri, 30 Oct 2026 23:02:38 -0300 Subject: [PATCH 2/2] test: add CNH generator exhaustion test --- coverage/lcov.info | 1468 +++++++++++++++++++++++++++----------------- test/cnh_test.dart | 10 + 2 files changed, 930 insertions(+), 548 deletions(-) diff --git a/coverage/lcov.info b/coverage/lcov.info index eab2ddd..92b04df 100644 --- a/coverage/lcov.info +++ b/coverage/lcov.info @@ -1,673 +1,1045 @@ -SF:lib/src/formatters/altura_input_formatter.dart +SF:lib\src\formatters\altura_input_formatter.dart DA:5,1 -DA:9,3 -DA:12,2 -DA:13,3 -DA:14,1 -DA:19,1 +DA:8,2 +DA:10,3 +DA:13,2 +DA:14,3 +DA:15,1 +DA:20,1 DA:22,3 DA:23,7 DA:24,3 DA:25,5 DA:28,1 -DA:29,1 DA:30,2 LF:13 LH:13 end_of_record -SF:lib/src/formatters/cartao_bancario_input_formatter.dart -DA:5,2 -DA:9,6 -DA:11,4 -DA:12,2 -DA:13,8 -DA:15,6 -DA:16,2 -DA:18,3 -DA:21,6 -DA:24,2 -DA:25,2 -DA:26,2 -LF:12 -LH:12 -end_of_record -SF:lib/src/formatters/centavos_input_formatter.dart -DA:9,1 +SF:lib\src\formatters\cartao_bancario_input_formatter.dart +DA:5,1 +DA:8,2 DA:10,3 -DA:11,0 +DA:12,2 +DA:13,1 +DA:14,4 +DA:15,3 DA:16,1 -DA:19,5 -DA:21,1 +DA:17,2 +DA:20,3 DA:23,1 +DA:24,1 +DA:25,1 +LF:13 +LH:13 +end_of_record +SF:lib\src\formatters\celular_input_formatter.dart +DA:5,1 +DA:8,1 +LF:2 +LH:2 +end_of_record +SF:lib\src\formatters\centavos_input_formatter.dart +DA:10,2 +DA:11,6 +DA:12,0 +DA:17,2 +DA:20,4 +DA:21,4 +DA:22,6 DA:24,2 DA:26,2 -DA:27,6 -DA:28,3 -DA:29,1 -DA:30,4 -DA:31,4 -DA:34,1 -DA:39,1 -DA:44,3 -DA:45,1 -DA:46,1 -DA:47,0 +DA:27,4 +DA:29,4 +DA:30,10 +DA:31,6 +DA:32,2 +DA:33,8 +DA:34,8 +DA:37,4 +DA:42,6 +DA:47,6 +DA:48,1 DA:49,1 -DA:51,1 +DA:50,1 DA:52,1 -DA:53,2 -DA:58,2 -DA:59,2 -DA:60,1 -DA:62,1 -DA:66,2 -DA:67,2 -DA:68,1 -DA:70,0 -DA:74,1 -DA:75,1 -DA:80,1 -DA:81,0 -DA:83,1 -DA:86,1 -DA:87,0 -DA:89,1 -DA:91,1 -DA:92,1 +DA:54,1 +DA:55,1 +DA:56,2 +DA:61,4 +DA:62,2 +DA:63,1 +DA:65,1 +DA:69,4 +DA:70,2 +DA:71,1 +DA:73,0 +DA:77,2 +DA:78,2 +DA:81,2 +DA:82,6 +DA:84,2 +DA:87,2 +DA:88,2 +DA:90,2 +DA:92,2 DA:93,2 -LF:43 -LH:38 +DA:94,4 +LF:45 +LH:43 end_of_record -SF:lib/src/formatters/cep_input_formatter.dart -DA:9,2 -DA:11,2 -DA:15,6 -DA:17,2 -DA:18,4 -DA:20,8 +SF:lib\src\formatters\cep_input_formatter.dart +DA:9,1 +DA:11,1 +DA:14,2 +DA:16,3 +DA:18,1 +DA:19,2 DA:21,4 DA:22,2 -DA:23,3 -DA:25,2 -DA:26,2 -DA:27,3 -DA:29,6 -DA:32,2 -DA:33,2 -DA:34,2 -LF:16 -LH:16 +DA:23,1 +DA:24,2 +DA:26,1 +DA:27,1 +DA:28,2 +DA:30,3 +DA:33,1 +DA:34,1 +DA:35,1 +LF:17 +LH:17 end_of_record -SF:lib/src/formatters/cest_input_formatter.dart -DA:5,2 -DA:9,6 -DA:11,4 +SF:lib\src\formatters\cert_nascimento_input_formatter.dart +DA:6,1 +DA:9,2 +DA:11,3 DA:13,2 -DA:15,6 -DA:16,8 -DA:17,7 -DA:20,6 -DA:21,8 -DA:22,7 -DA:25,6 -DA:26,6 -DA:29,2 -DA:30,2 -DA:31,2 -LF:15 -LH:15 +DA:15,1 +DA:17,3 +DA:18,4 +DA:19,4 +DA:21,3 +DA:22,4 +DA:23,4 +DA:25,3 +DA:26,4 +DA:27,4 +DA:29,3 +DA:30,4 +DA:31,4 +DA:33,3 +DA:34,4 +DA:35,4 +DA:37,3 +DA:38,4 +DA:39,4 +DA:41,3 +DA:42,4 +DA:43,4 +DA:45,3 +DA:46,4 +DA:47,4 +DA:49,3 +DA:50,3 +DA:53,1 +DA:54,1 +DA:55,1 +LF:34 +LH:34 end_of_record -SF:lib/src/formatters/cnpj_input_formatter.dart +SF:lib\src\formatters\cest_input_formatter.dart +DA:5,1 DA:8,2 -DA:11,2 -DA:14,4 -DA:16,4 +DA:10,3 +DA:12,2 +DA:14,1 +DA:16,3 +DA:17,4 DA:18,4 -DA:20,2 -DA:22,2 -DA:23,8 -DA:24,7 -DA:26,2 -DA:27,8 -DA:28,7 -DA:30,2 -DA:31,8 -DA:32,7 -DA:34,2 -DA:35,8 -DA:36,7 -DA:38,2 -DA:39,6 -DA:42,2 +DA:21,3 +DA:22,4 +DA:23,4 +DA:26,3 +DA:27,3 +DA:30,1 +DA:31,1 +DA:32,1 +LF:16 +LH:16 +end_of_record +SF:lib\src\formatters\cnh_input_formatter.dart +DA:10,1 +DA:12,1 +DA:17,2 +DA:19,4 +LF:4 +LH:4 +end_of_record +SF:lib\src\formatters\cnpj_input_formatter.dart +DA:7,2 +DA:10,2 +DA:13,4 +DA:15,4 +DA:17,4 +DA:19,4 +DA:21,2 +DA:23,2 +DA:24,8 +DA:25,8 +DA:27,2 +DA:28,8 +DA:29,8 +DA:31,2 +DA:32,8 +DA:33,8 +DA:35,2 +DA:36,8 +DA:37,8 +DA:39,2 +DA:40,6 DA:43,2 DA:44,2 -LF:23 -LH:23 +DA:45,2 +LF:24 +LH:24 end_of_record -SF:lib/src/formatters/cns_formatter.dart -DA:5,2 -DA:9,6 -DA:11,4 -DA:13,2 -DA:15,6 -DA:16,8 -DA:17,7 -DA:19,6 -DA:20,8 -DA:21,7 -DA:23,6 -DA:24,8 -DA:25,7 -DA:27,6 -DA:28,6 +SF:lib\src\formatters\cnpj_alfanumerico_input_formatter.dart +DA:17,2 +DA:20,2 +DA:23,4 +DA:25,4 +DA:27,4 +DA:29,4 DA:31,2 -DA:32,2 DA:33,2 -LF:18 -LH:18 +DA:34,8 +DA:35,8 +DA:37,2 +DA:38,8 +DA:39,8 +DA:41,2 +DA:42,8 +DA:43,8 +DA:45,2 +DA:46,8 +DA:47,8 +DA:49,2 +DA:50,6 +DA:53,2 +DA:54,4 +DA:55,2 +LF:24 +LH:24 +end_of_record +SF:lib\src\formatters\cns_formatter.dart +DA:5,1 +DA:8,2 +DA:10,3 +DA:12,2 +DA:14,1 +DA:16,3 +DA:17,4 +DA:18,4 +DA:20,3 +DA:21,4 +DA:22,4 +DA:24,3 +DA:25,4 +DA:26,4 +DA:28,3 +DA:29,3 +DA:32,1 +DA:33,1 +DA:34,1 +LF:19 +LH:19 end_of_record -SF:lib/src/formatters/compound_formatters/cpf_ou_cpnj_formatter.dart -DA:6,5 +SF:lib\src\formatters\compound_formatters\cpf_ou_cpnj_formatter.dart +DA:7,5 LF:1 LH:1 end_of_record -SF:lib/src/formatters/cpf_input_formatter.dart -DA:8,2 -DA:11,2 +SF:lib\src\formatters\compound_formatters\cpf_ou_cpnj_alfanumerico_formatter.dart +DA:7,1 +DA:8,4 +LF:2 +LH:2 +end_of_record +SF:lib\src\formatters\cpf_input_formatter.dart +DA:7,2 +DA:10,2 +DA:13,4 DA:15,8 DA:17,4 DA:19,2 DA:21,6 DA:22,8 -DA:23,7 +DA:23,8 DA:25,6 DA:26,8 -DA:27,7 +DA:27,8 DA:29,6 DA:30,8 -DA:31,7 +DA:31,8 DA:33,6 DA:34,6 DA:37,2 -DA:38,2 +DA:38,4 DA:39,2 -LF:19 -LH:19 +LF:20 +LH:20 end_of_record -SF:lib/src/formatters/data_input_formatter.dart +SF:lib\src\formatters\data_input_formatter.dart DA:5,1 -DA:9,3 -DA:11,2 -DA:13,1 -DA:15,3 -DA:16,4 +DA:8,2 +DA:10,3 +DA:12,2 +DA:14,1 +DA:16,3 DA:17,4 -DA:19,3 -DA:20,4 +DA:18,4 +DA:20,3 DA:21,4 -DA:23,3 +DA:22,4 DA:24,3 -DA:27,1 +DA:25,3 DA:28,1 DA:29,1 -LF:15 -LH:15 -end_of_record -SF:lib/src/formatters/hora_input_formatter.dart -DA:5,1 -DA:9,3 -DA:11,2 -DA:13,1 -DA:15,2 -DA:16,1 -DA:17,0 -DA:18,0 -DA:20,1 -DA:21,0 -DA:22,0 -DA:24,1 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 DA:30,1 -DA:31,3 -DA:32,1 -DA:33,4 -DA:34,4 -DA:38,3 -DA:39,3 -DA:42,1 -DA:43,1 -DA:44,1 -LF:26 -LH:18 +LF:16 +LH:16 end_of_record -SF:lib/src/formatters/iof_input_formatter.dart -DA:5,2 -DA:8,4 -DA:11,2 -DA:13,2 -DA:15,2 -DA:17,2 -DA:19,10 -DA:22,2 -DA:23,2 -DA:24,4 -LF:10 -LH:10 -end_of_record -SF:lib/src/formatters/km_input_formatter.dart -DA:5,1 -DA:9,3 -DA:11,2 -DA:13,1 -DA:15,2 +SF:lib\src\formatters\hora_input_formatter.dart +DA:7,1 +DA:10,2 +DA:12,3 +DA:14,2 DA:16,1 -DA:17,1 -DA:18,5 -DA:19,1 +DA:18,7 +DA:20,2 DA:21,1 -DA:22,1 -DA:23,5 -DA:24,1 -DA:26,1 -DA:27,1 -DA:28,5 -DA:29,1 -DA:34,3 -DA:35,3 +DA:22,3 +DA:23,1 +DA:25,1 +DA:26,2 +DA:28,1 +DA:29,2 +DA:30,3 +DA:31,1 +DA:32,4 +DA:33,4 +DA:35,1 +DA:36,2 +DA:37,3 DA:38,1 -DA:39,1 -DA:40,1 -LF:22 -LH:22 +DA:39,4 +DA:40,4 +DA:44,3 +DA:45,3 +DA:48,1 +DA:49,1 +DA:50,1 +LF:29 +LH:29 end_of_record -SF:lib/src/formatters/ncm_input_formatter.dart +SF:lib\src\formatters\iof_input_formatter.dart DA:5,1 -DA:9,3 -DA:11,2 -DA:13,1 -DA:15,3 -DA:16,4 -DA:17,4 -DA:19,3 -DA:20,4 -DA:21,4 -DA:24,3 -DA:25,3 +DA:8,2 +DA:10,2 +DA:12,1 +DA:14,1 +DA:16,1 +DA:18,1 +DA:20,5 +DA:23,1 +DA:25,2 +LF:10 +LH:10 +end_of_record +SF:lib\src\formatters\km_input_formatter.dart +DA:5,1 +DA:8,2 +DA:10,3 +DA:12,2 +DA:15,1 +DA:17,2 +DA:18,1 +DA:20,1 +DA:21,5 +DA:23,1 +DA:25,1 +DA:26,5 DA:28,1 -DA:29,1 DA:30,1 -LF:15 -LH:15 +DA:31,5 +DA:36,1 +DA:37,1 +DA:40,3 +DA:41,3 +DA:44,1 +DA:45,1 +DA:46,1 +LF:22 +LH:22 end_of_record -SF:lib/src/formatters/peso_input_formatter.dart +SF:lib\src\formatters\ncm_input_formatter.dart DA:5,1 -DA:9,3 -DA:11,2 -DA:13,1 -DA:15,2 -DA:16,1 +DA:8,2 +DA:10,3 +DA:12,2 +DA:14,1 +DA:16,3 DA:17,4 DA:18,4 -DA:20,1 +DA:20,3 DA:21,4 DA:22,4 +DA:25,3 DA:26,3 -DA:27,3 +DA:29,1 DA:30,1 DA:31,1 -DA:32,1 LF:16 LH:16 end_of_record -SF:lib/src/formatters/placa_veiculo_formatter.dart +SF:lib\src\formatters\nup_input_formatter.dart DA:8,1 -DA:12,3 -DA:14,2 -DA:16,1 -DA:18,3 -DA:19,0 -DA:20,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:28,3 -DA:29,3 -DA:32,1 -DA:33,2 -DA:34,1 -LF:15 -LH:10 +DA:11,2 +DA:13,3 +DA:15,2 +DA:17,1 +DA:19,3 +DA:20,4 +DA:21,4 +DA:23,3 +DA:24,4 +DA:25,4 +DA:27,3 +DA:28,4 +DA:29,4 +DA:31,3 +DA:32,4 +DA:33,4 +DA:35,3 +DA:36,4 +DA:37,4 +DA:40,3 +DA:41,3 +DA:44,1 +DA:45,1 +DA:46,1 +LF:25 +LH:25 end_of_record -SF:lib/src/formatters/real_input_formatter.dart +SF:lib\src\formatters\peso_input_formatter.dart +DA:5,1 DA:8,2 +DA:10,3 DA:12,2 -DA:16,6 +DA:14,1 +DA:16,2 +DA:17,1 DA:18,4 -DA:20,2 -DA:22,2 +DA:19,4 +DA:21,1 +DA:22,4 DA:23,4 -DA:25,6 -DA:28,2 -DA:29,2 -DA:30,4 -LF:11 -LH:11 +DA:27,3 +DA:28,3 +DA:31,1 +DA:32,1 +DA:33,1 +LF:17 +LH:17 end_of_record -SF:lib/src/formatters/telefone_input_formatter.dart +SF:lib\src\formatters\pis_pasep_input_formatter.dart DA:7,1 -DA:10,2 -DA:13,1 -DA:16,1 -DA:17,4 -DA:22,2 -DA:24,1 -DA:26,1 -DA:27,1 +DA:9,1 +DA:14,2 +DA:16,4 +DA:18,2 +DA:20,1 +DA:22,3 +DA:23,4 +DA:24,4 +DA:26,3 +DA:27,4 DA:28,4 -DA:31,1 +DA:30,3 +DA:31,4 DA:32,4 -DA:33,4 -DA:36,3 -DA:37,1 -DA:38,4 -DA:39,4 -DA:42,1 -DA:43,4 -DA:44,4 -DA:48,1 -DA:49,3 -DA:52,1 -DA:53,1 -DA:54,1 -LF:25 -LH:25 +DA:34,3 +DA:35,3 +DA:38,1 +DA:39,1 +DA:40,1 +LF:20 +LH:20 end_of_record -SF:lib/src/formatters/temperatura_input_formatter.dart -DA:5,1 -DA:10,2 -DA:12,1 -DA:15,2 -DA:17,1 -DA:20,1 -DA:21,0 -DA:22,0 +SF:lib\src\formatters\placa_veiculo_formatter.dart +DA:8,1 +DA:13,2 +DA:15,4 +DA:17,2 +DA:19,1 +DA:21,3 +DA:22,2 +DA:23,0 +DA:25,1 +DA:26,7 +DA:28,1 +DA:32,3 +DA:33,3 +DA:36,1 +DA:37,2 +DA:38,1 +LF:16 +LH:15 +end_of_record +SF:lib\src\formatters\real_input_formatter.dart +DA:9,1 +DA:13,1 +DA:16,2 +DA:18,3 +DA:20,2 +DA:22,1 DA:24,1 DA:25,4 -DA:26,4 +DA:27,3 DA:30,1 -DA:31,3 +DA:31,1 +DA:32,2 +LF:12 +LH:12 +end_of_record +SF:lib\src\formatters\telefone_input_formatter.dart +DA:12,1 +DA:17,2 +DA:18,6 +DA:21,1 +LF:4 +LH:4 +end_of_record +SF:lib\src\formatters\telefone_fixo_input_formatter.dart +DA:5,1 +DA:8,1 +LF:2 +LH:2 +end_of_record +SF:lib\src\formatters\telefone_ou_celular_input_formatter.dart +DA:14,1 +DA:19,2 +DA:20,5 +DA:21,2 +DA:22,2 +LF:5 +LH:5 +end_of_record +SF:lib\src\formatters\temperatura_input_formatter.dart +DA:6,1 +DA:9,2 +DA:11,3 +DA:13,2 +DA:15,1 +DA:17,2 +DA:18,1 +DA:19,4 +DA:20,4 +DA:22,1 +DA:23,4 +DA:24,4 +DA:28,3 +DA:29,3 +DA:32,1 +DA:33,1 DA:34,1 -DA:35,1 -DA:36,1 -LF:16 -LH:14 +LF:17 +LH:17 end_of_record -SF:lib/src/formatters/validade_cartao_input_formatter.dart +SF:lib\src\formatters\validade_cartao_input_formatter.dart +DA:9,1 +DA:10,3 DA:11,1 -DA:12,2 -DA:13,0 -DA:15,1 +DA:13,1 +DA:16,2 DA:18,2 DA:20,2 -DA:24,2 +DA:22,2 +DA:24,1 DA:26,1 -DA:28,1 -DA:29,4 -DA:30,4 -DA:33,1 -DA:34,3 +DA:27,4 +DA:28,4 +DA:31,1 +DA:32,3 +DA:35,1 +DA:36,1 DA:37,1 -DA:38,1 -DA:39,1 -LF:16 -LH:15 +LF:17 +LH:17 end_of_record -SF:lib/src/modelos/meses.dart -DA:6,0 -DA:21,0 -LF:2 -LH:0 -end_of_record -SF:lib/src/modelos/semana.dart -DA:19,0 -DA:27,0 -DA:35,0 -DA:62,0 -DA:72,0 -DA:82,0 -LF:6 -LH:0 +SF:lib\src\modelos\meses.dart +DA:20,2 +LF:1 +LH:1 end_of_record -SF:lib/src/util/extensores.dart -DA:5,1 -DA:6,6 -DA:10,3 -DA:15,2 -DA:17,1 -DA:22,1 -DA:23,2 -DA:28,1 -DA:29,6 -DA:33,3 -DA:38,2 -DA:40,1 -DA:45,1 -DA:46,2 +SF:lib\src\util\extensores.dart +DA:7,2 +DA:8,10 +DA:13,6 +DA:18,2 +DA:20,1 +DA:25,1 +DA:26,2 +DA:31,1 +DA:32,6 +DA:35,3 +DA:40,4 +DA:42,2 +DA:47,2 +DA:48,4 LF:14 LH:14 end_of_record -SF:lib/src/util/util_brasil_fields.dart -DA:6,1 -DA:7,2 -DA:8,2 -DA:12,1 -DA:13,2 -DA:14,1 +SF:lib\src\util\util_brasil_fields.dart +DA:7,3 +DA:10,2 +DA:11,2 +DA:12,0 +DA:14,4 DA:18,1 -DA:19,2 -DA:20,1 -DA:21,3 +DA:19,1 +DA:20,0 +DA:22,2 +DA:28,1 DA:29,1 -DA:31,3 -DA:34,3 -DA:35,5 -DA:43,1 -DA:45,3 -DA:47,1 -DA:50,5 -DA:53,2 -DA:54,0 +DA:30,0 +DA:32,1 +DA:34,2 +DA:35,1 +DA:36,1 +DA:45,1 +DA:46,2 +DA:47,0 DA:55,4 -DA:57,5 -DA:60,2 -DA:61,0 -DA:62,3 -DA:66,1 -DA:67,3 -DA:70,1 -DA:74,2 -DA:77,2 -DA:80,0 +DA:56,4 +DA:65,1 +DA:70,2 +DA:71,0 +DA:77,1 +DA:80,4 DA:81,0 -DA:84,0 -DA:85,0 -DA:88,1 -DA:89,1 -DA:90,1 -DA:94,1 -DA:95,1 -DA:96,1 -DA:100,1 -DA:103,1 -DA:105,2 +DA:88,2 +DA:89,0 +DA:90,4 +DA:92,4 +DA:93,0 +DA:100,2 +DA:101,0 +DA:102,3 DA:108,1 -DA:109,1 -DA:111,3 -DA:112,1 -DA:115,1 -DA:119,1 -DA:126,1 -DA:128,2 +DA:109,2 +DA:110,0 +DA:117,1 +DA:121,2 +DA:124,2 DA:129,1 -DA:130,1 DA:131,1 -DA:132,5 -DA:133,1 -DA:134,6 -DA:135,1 -DA:136,7 -DA:138,1 -LF:60 -LH:54 -end_of_record -SF:lib/src/util/util_data.dart -DA:7,0 -DA:8,0 -DA:12,0 -DA:13,0 -DA:15,0 -DA:16,0 -DA:18,0 -DA:22,0 -DA:26,1 -DA:27,1 -DA:28,2 -DA:32,1 -DA:33,1 +DA:132,1 +DA:135,2 +DA:138,2 +DA:141,1 +DA:142,1 +DA:148,2 +DA:149,2 +DA:156,1 +DA:162,1 +DA:166,1 +DA:172,1 +DA:173,1 +DA:176,0 +DA:177,0 +DA:178,0 +DA:180,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:189,0 +DA:196,1 +DA:197,1 +DA:198,0 +DA:201,2 +DA:202,2 +DA:209,1 +DA:210,1 +DA:211,0 +DA:213,2 +DA:217,1 +DA:218,1 +DA:219,0 +DA:221,2 +DA:225,0 +DA:226,0 +DA:227,0 +DA:229,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:237,0 +DA:243,1 +DA:246,1 +DA:248,2 +DA:251,1 +DA:252,1 +DA:254,3 +DA:255,1 +DA:258,1 +DA:262,1 +DA:269,1 +DA:270,1 +DA:271,1 +DA:277,2 +LF:97 +LH:68 +end_of_record +SF:lib\src\util\util_data.dart +DA:8,1 +DA:10,1 +DA:18,2 +DA:19,2 +DA:20,4 +DA:22,4 +DA:23,4 +DA:24,4 +DA:25,6 +DA:27,2 +DA:28,4 +DA:29,4 +DA:30,4 DA:34,2 -DA:38,1 -DA:39,1 +DA:35,2 +DA:37,4 +DA:38,4 DA:40,2 -DA:44,1 -DA:45,1 -DA:46,2 -DA:50,1 -DA:51,1 -DA:52,2 -DA:56,0 -DA:57,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:67,0 -DA:70,0 -DA:75,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:79,0 -DA:80,0 -DA:82,0 -DA:87,0 -DA:90,0 -DA:97,1 -DA:98,1 -DA:99,2 -DA:105,1 -DA:106,1 -DA:107,3 -DA:113,1 -DA:114,1 -DA:115,2 -LF:50 -LH:24 +DA:44,2 +DA:48,1 +DA:49,1 +DA:50,2 +DA:54,1 +DA:55,1 +DA:56,2 +DA:60,1 +DA:61,1 +DA:62,2 +DA:66,1 +DA:67,1 +DA:68,2 +DA:72,1 +DA:73,1 +DA:74,2 +DA:80,1 +DA:81,1 +DA:83,2 +DA:84,1 +DA:85,2 +DA:86,2 +DA:87,2 +DA:91,0 +DA:94,2 +DA:101,2 +DA:102,2 +DA:103,4 +DA:104,2 +DA:105,4 +DA:106,4 +DA:108,4 +DA:113,0 +DA:116,2 +DA:121,1 +DA:122,1 +DA:123,2 +DA:127,1 +DA:128,1 +DA:129,3 +DA:133,1 +DA:134,1 +DA:135,2 +LF:61 +LH:59 end_of_record -SF:lib/src/formatters/adiciona_separador.dart -DA:1,3 -DA:4,15 -DA:5,3 -DA:6,3 -DA:9,3 -DA:10,6 +SF:lib\src\formatters\adiciona_separador.dart +DA:2,4 +DA:5,20 +DA:6,4 +DA:7,4 +DA:10,4 +DA:11,8 LF:6 LH:6 end_of_record -SF:lib/src/formatters/compound_formatters/compound_formatter.dart +SF:lib\src\formatters\telefone_mask_input_formatter.dart +DA:3,3 +DA:13,1 +DA:18,2 +DA:19,4 +DA:22,1 +DA:24,2 +DA:25,1 +DA:28,3 +DA:29,4 +DA:32,2 +DA:33,3 +DA:34,1 +DA:35,3 +DA:39,3 +DA:40,3 +DA:43,1 +DA:44,5 +DA:45,1 +DA:46,3 +DA:47,1 +DA:50,1 +DA:51,3 +DA:52,3 +DA:53,1 +DA:54,1 +DA:55,1 +DA:60,1 +DA:62,1 +LF:28 +LH:28 +end_of_record +SF:lib\src\formatters\compound_formatters\compound_formatter.dart DA:11,1 DA:12,2 DA:13,3 DA:15,1 -DA:18,3 -DA:19,2 -DA:20,1 -DA:21,1 -DA:22,0 -DA:23,0 -DA:25,1 -LF:11 -LH:9 +DA:18,2 +DA:20,3 +DA:21,2 +DA:22,1 +DA:23,1 +DA:24,0 +DA:25,0 +DA:27,1 +LF:12 +LH:10 end_of_record -SF:lib/src/validators/cnpj_validator.dart -DA:24,2 -DA:28,14 -DA:32,4 -DA:33,4 -DA:34,4 -DA:37,2 -DA:39,4 -DA:42,2 -DA:43,2 -DA:45,4 -DA:46,14 -DA:49,2 -DA:50,2 +SF:lib\src\validators\cnh_validator.dart +DA:31,3 +DA:35,6 +DA:36,6 +DA:37,9 +DA:38,9 +DA:41,3 +DA:42,3 +DA:43,3 +DA:47,3 +DA:48,3 +DA:51,3 +DA:52,3 +DA:53,1 +DA:57,3 +DA:61,3 +DA:65,3 +DA:66,3 +DA:69,3 +DA:76,3 +DA:78,3 +DA:81,3 +DA:85,6 +DA:89,6 +DA:93,3 +DA:97,12 +DA:101,2 +DA:102,1 +DA:104,3 +DA:107,4 +DA:108,6 +DA:111,2 +DA:116,2 +DA:117,2 +DA:122,1 +LF:34 +LH:34 +end_of_record +SF:lib\src\validators\cnpj_alfanumerico_validator.dart +DA:24,4 +DA:25,8 +DA:26,8 +DA:33,3 +DA:37,24 +DA:41,6 +DA:42,6 +DA:43,6 +DA:46,3 +DA:48,6 +DA:52,2 DA:53,2 -DA:56,2 -DA:58,2 -DA:62,2 -DA:67,4 -DA:72,2 -DA:76,2 -DA:77,6 -DA:78,6 -DA:80,8 -DA:81,6 -DA:84,1 -DA:87,2 -DA:88,4 -DA:91,3 +DA:57,4 +DA:59,14 +DA:64,3 +DA:65,3 +DA:68,3 +DA:75,3 +DA:77,3 +DA:80,3 +DA:84,6 +DA:88,6 DA:92,3 -DA:94,1 -LF:30 -LH:30 +DA:96,3 +DA:97,9 +DA:98,9 +DA:100,12 +DA:101,9 +DA:105,2 +DA:106,0 +DA:108,3 +DA:111,4 +DA:112,12 +DA:115,6 +DA:116,6 +DA:118,2 +DA:119,2 +DA:123,1 +LF:38 +LH:37 end_of_record -SF:lib/src/validators/cpf_validator.dart -DA:25,2 -DA:27,10 -DA:29,4 -DA:31,2 -DA:33,6 -DA:34,8 -DA:37,8 -DA:39,4 -DA:42,2 -DA:43,2 -DA:45,4 -DA:46,12 +SF:lib\src\validators\cnpj_validator.dart +DA:27,3 +DA:30,15 +DA:34,6 +DA:35,6 +DA:36,6 +DA:39,3 +DA:41,6 +DA:45,2 +DA:46,2 +DA:48,4 +DA:50,14 +DA:55,3 +DA:56,3 +DA:59,3 +DA:66,3 +DA:68,3 +DA:71,3 +DA:75,6 +DA:79,6 +DA:83,3 +DA:87,3 +DA:88,9 +DA:89,9 +DA:91,12 +DA:92,9 +DA:96,2 +DA:97,1 +DA:99,3 +DA:102,4 +DA:103,6 +DA:106,6 +DA:107,6 +DA:109,2 +DA:110,2 +DA:114,1 +LF:35 +LH:35 +end_of_record +SF:lib\src\validators\cpf_validator.dart +DA:28,3 +DA:30,15 +DA:32,6 +DA:34,3 +DA:36,9 +DA:37,12 +DA:40,12 +DA:42,6 +DA:46,2 +DA:47,2 DA:49,2 -DA:50,2 -DA:53,2 -DA:56,2 -DA:58,2 -DA:62,2 -DA:67,4 -DA:72,2 -DA:76,2 -DA:77,6 -DA:78,6 -DA:80,8 -DA:81,6 -DA:84,1 -DA:87,2 -DA:88,4 -DA:91,3 -DA:92,3 -DA:94,1 -LF:31 -LH:31 +DA:51,14 +DA:55,3 +DA:56,3 +DA:59,3 +DA:66,3 +DA:68,3 +DA:71,3 +DA:75,6 +DA:79,6 +DA:83,3 +DA:87,3 +DA:88,9 +DA:89,9 +DA:91,12 +DA:92,9 +DA:96,3 +DA:97,2 +DA:99,4 +DA:102,6 +DA:103,9 +DA:106,9 +DA:107,9 +DA:109,3 +DA:110,2 +DA:114,1 +LF:36 +LH:36 +end_of_record +SF:lib\src\validators\nup_validator.dart +DA:9,3 +DA:11,3 +DA:14,3 +DA:18,6 +DA:22,6 +DA:26,3 +DA:27,9 +DA:31,3 +DA:32,3 +DA:35,3 +DA:40,3 +DA:41,3 +DA:42,3 +DA:43,3 +DA:44,3 +DA:45,3 +DA:47,6 +DA:48,9 +DA:49,9 +DA:51,3 +DA:52,6 +DA:56,1 +DA:57,1 +DA:59,2 +DA:61,8 +LF:25 +LH:25 +end_of_record +SF:lib\src\validators\pis_pasep_validator.dart +DA:28,3 +DA:31,9 +DA:32,15 +DA:35,3 +DA:37,6 +DA:41,2 +DA:42,2 +DA:44,2 +DA:46,14 +DA:50,3 +DA:51,3 +DA:54,3 +DA:61,3 +DA:63,3 +DA:66,3 +DA:70,6 +DA:74,6 +DA:78,3 +DA:82,12 +DA:86,2 +DA:87,1 +DA:89,3 +DA:92,4 +DA:93,6 +DA:95,6 +DA:97,2 +DA:98,2 +DA:102,1 +LF:28 +LH:28 end_of_record diff --git a/test/cnh_test.dart b/test/cnh_test.dart index f74bb65..9b5c5b9 100644 --- a/test/cnh_test.dart +++ b/test/cnh_test.dart @@ -142,6 +142,16 @@ void main() { expect(random.requestedMaxValues, hasLength(18)); }); + test('CNH generator throws after exhausting all attempts', () { + // Sempre retorna 0 → base 000000000 → blockList em toda tentativa. + final alwaysZero = RecordingRandom.constant(0); + + expect( + () => CNHValidator.generate(random: alwaysZero), + throwsStateError, + ); + }); + test('CNH generator agrees with an independent check-digit calculation', () { final random = Random(7);