Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
martijn00 committed Jul 27, 2024
1 parent 8dd189f commit 9c55b31
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 22 deletions.
56 changes: 34 additions & 22 deletions lib/src/usecase/profanity_validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ class ProfanityValidator extends BaseValidator<String> {
this.useAllLocales = false,
super.errorText,
super.checkNullOrEmpty,
}) : profanityList = profanityList ??
}) : customErrorText = errorText,
profanityList = profanityList ??
(useAllLocales
? Profanity.profanityListAll()
: Profanity.of(FormBuilderLocalizations.current.localeName)
Expand All @@ -33,51 +34,62 @@ class ProfanityValidator extends BaseValidator<String> {
/// Whether to use all locales to check for profanity.
final bool useAllLocales;

/// The custom error message returned if the value is invalid.
final String? customErrorText;

@override
String get translatedErrorText => FormBuilderLocalizations.current
.profanityErrorText(profanityList.join(', '));

Check warning on line 42 in lib/src/usecase/profanity_validator.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/usecase/profanity_validator.dart#L40-L42

Added lines #L40 - L42 were not covered by tests

@override
String? validateValue(String valueCandidate) {
if (profanityList.contains(valueCandidate)) {
return errorText;
final List<String> profanityFound = profanityAllFound(valueCandidate);
if (profanityFound.isNotEmpty) {
return customErrorText ??
FormBuilderLocalizations.current
.profanityErrorText(profanityFound.join(', '));
}

return null;
}

/// Returns a list of profanity words found in the value.
List<String> profanityAllFound(String valueCandidate) {
List<String> inputSplit = valueCandidate.split(' ');
final RegExp regExp = RegExp(r'\b\w+\b');
final List<String> inputSplit = regExp
.allMatches(valueCandidate)
.map((RegExpMatch match) => match.group(0)!.toLowerCase())
.toList();

List<String> profanityFound = <String>[];
for (String word in profanityList) {
for (int i = 0; i < inputSplit.length; i++) {
if (inputSplit[i].toLowerCase() == word) {
profanityFound.add(word);
}
if (inputSplit.contains(word.toLowerCase())) {
profanityFound.add(word);
}
}
return profanityFound;
}

/// Returns the value with profanity words censored.
String profanityCensored(String valueCandidate, {String? replaceWith}) {
List<String> inputSplit = valueCandidate.split(' ');
for (String word in profanityList) {
if (replaceWith == null) {
for (int i = 0; i < inputSplit.length; i++) {
if (inputSplit[i].toLowerCase() == word) {
inputSplit[i] = '*' * word.length;
}
}
final RegExp regExp = RegExp(r'\b\w+\b');
final Iterable<RegExpMatch> matches = regExp.allMatches(valueCandidate);

StringBuffer buffer = StringBuffer();
int lastMatchEnd = 0;

for (final RegExpMatch match in matches) {
buffer.write(valueCandidate.substring(lastMatchEnd, match.start));
String word = match.group(0)!;
if (profanityList.contains(word.toLowerCase())) {
buffer.write(replaceWith ?? '*' * word.length);
} else {
for (int i = 0; i < inputSplit.length; i++) {
if (inputSplit[i].toLowerCase() == word) {
inputSplit[i] = replaceWith;
}
}
buffer.write(word);
}
lastMatchEnd = match.end;
}
return inputSplit.join(' ');

buffer.write(valueCandidate.substring(lastMatchEnd));
return buffer.toString();
}
}
161 changes: 161 additions & 0 deletions test/src/usecase/profanity_validator_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:form_builder_validators/form_builder_validators.dart';

void main() {
final List<String> profanityList = <String>[
'badword1',
'badword2',
'badword3',
];
const String customErrorMessage =
'This text contains inappropriate language.';

group('ProfanityValidator -', () {
test('should return null for strings without profanity', () {
// Arrange
final ProfanityValidator validator =
ProfanityValidator(profanityList: profanityList);

// Act & Assert
expect(validator.validate('This is a clean sentence.'), isNull);
expect(validator.validate('Nothing wrong here!'), isNull);
});

test('should return the default error message for strings with profanity',
() {
// Arrange
final ProfanityValidator validator =
ProfanityValidator(profanityList: profanityList);
const String badSentence = 'This is badword1.';
const String badSentence2 = 'Another badword2 example.';

// Act & Assert
expect(
validator.validate(badSentence),
equals(
FormBuilderLocalizations.current.profanityErrorText(
validator.profanityAllFound(badSentence).join(', ')),
),
);
expect(
validator.validate(badSentence2),
equals(
FormBuilderLocalizations.current.profanityErrorText(
validator.profanityAllFound(badSentence2).join(', ')),
),
);
});

test('should return the custom error message for strings with profanity',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
errorText: customErrorMessage,
);

// Act & Assert
expect(
validator.validate('This is badword1.'), equals(customErrorMessage));
expect(validator.validate('Another badword2 example.'),
equals(customErrorMessage));
});

test(
'should return null when the value is an empty string and null check is disabled',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
checkNullOrEmpty: false,
);
const String value = '';

// Act & Assert
final String? result = validator.validate(value);
expect(result, isNull);
});

test('should return null when the value is null and null check is disabled',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
checkNullOrEmpty: false,
);
const String? value = null;

// Act & Assert
final String? result = validator.validate(value);
expect(result, isNull);
});

test('should return a list of profanity words found in the string', () {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
);

// Act & Assert
expect(validator.profanityAllFound('This is badword1 and badword3.'),
equals(['badword1', 'badword3']));
expect(validator.profanityAllFound('Clean sentence.'), equals([]));
});

test('should return the string with profanity words censored', () {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
);

// Act & Assert
expect(validator.profanityCensored('This is badword1 and badword3.'),
equals('This is ******** and ********.'));
expect(validator.profanityCensored('Another badword2 example.'),
equals('Another ******** example.'));
});

test(
'should return the string with profanity words replaced with a custom string',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
profanityList: profanityList,
);

// Act & Assert
expect(
validator.profanityCensored('This is badword1 and badword3.',
replaceWith: '[censored]'),
equals('This is [censored] and [censored].'));
expect(
validator.profanityCensored('Another badword2 example.',
replaceWith: '[censored]'),
equals('Another [censored] example.'));
});

test(
'should use all locales for profanity check when useAllLocales is true',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator(
useAllLocales: true,
);

// Act & Assert
expect(validator.validate('This is shit.'), isNotNull);
expect(validator.validate('Another apeshit example.'), isNotNull);
});

test(
'should initialize with empty constructor and use default profanity list',
() {
// Arrange
final ProfanityValidator validator = ProfanityValidator();

// Act & Assert
expect(validator.validate('This is a clean sentence.'), isNull);
expect(validator.validate('shit'), isNotNull);
});
});
}

0 comments on commit 9c55b31

Please sign in to comment.