Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Purpose: Provide just enough project context so an AI assistant can make correct
### 4. Testing Strategy
* Unit tests in `test/unit/` mirror README examples & edge cases (list formats, depth, duplicates, charsets, null handling, custom hooks).
* E2E tests in `test/e2e/` exercise higher‑level URI extensions.
* Comparison tests (`test/comparison/`) ensure parity with the JS implementation via fixture JSON & a Node script; run `test/comparison/compare_outputs.sh` after semantic changes to core encode/decode logic.
* Comparison tests (`test/comparison/`) ensure Dart VM and dart2js parity with the JS implementation via fixture JSON & a Node script; run `test/comparison/compare_outputs.sh` after semantic changes to core encode/decode logic.
* When adding behavior, first add/modify a unit test replicating the JS `qs` behavior (consult upstream if uncertain), then adjust implementation.

### 5. Dev Workflow / Commands
Expand Down
11 changes: 1 addition & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,16 +237,7 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Run a comparison test between qs_dart and qs for JavaScript
working-directory: test/comparison
run: |
set -e
node_output=$(node qs.js)
dart_output=$(dart run qs.dart)
if [ "$node_output" == "$dart_output" ]; then
echo "The outputs are identical."
else
echo "The outputs are different."
exit 1
fi
run: bash compare_outputs.sh
ci_required:
name: "CI Required"
if: ${{ always() }}
Expand Down
44 changes: 30 additions & 14 deletions test/comparison/compare_outputs.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
#!/usr/bin/env bash

# Get the directory of the script
script_dir=$(dirname "$0")

# Run the JavaScript and Dart scripts and save their outputs
node_output=$(node "$script_dir/qs.js")
dart_output=$(dart run "$script_dir/qs.dart")

# Compare the outputs
if [ "$node_output" == "$dart_output" ]; then
echo "The outputs are identical."
exit 0
else
echo "The outputs are different."
set -euo pipefail

script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

compare_outputs() {
local expected="$1"
local actual="$2"
local description="$3"

if ! diff -u "$expected" "$actual"; then
echo "$description outputs are different." >&2
exit 1
fi
fi
}

node "$script_dir/qs.js" > "$tmp_dir/node.out"
dart run "$script_dir/qs.dart" > "$tmp_dir/dart_vm.out"

test_cases_base64=$(base64 < "$script_dir/test_cases.json" | tr -d '\r\n')
dart compile js -O2 \
-DQS_COMPARISON_TEST_CASES_BASE64="$test_cases_base64" \
"$script_dir/qs_dart2js.dart" \
-o "$tmp_dir/qs_dart2js.js"
node "$tmp_dir/qs_dart2js.js" > "$tmp_dir/dart2js.out"

compare_outputs "$tmp_dir/node.out" "$tmp_dir/dart_vm.out" "Node and Dart VM"
compare_outputs "$tmp_dir/node.out" "$tmp_dir/dart2js.out" "Node and dart2js"

echo "The Node, Dart VM, and dart2js outputs are identical."
15 changes: 15 additions & 0 deletions test/comparison/comparison.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'dart:convert' show jsonDecode, jsonEncode;

import 'package:qs_dart/qs_dart.dart' as qs;

Iterable<String> buildComparisonLines(String testCasesJson) sync* {
final List<Map<String, dynamic>> e2eTestCases =
List.from(jsonDecode(testCasesJson));

for (final Map<String, dynamic> testCase in e2eTestCases) {
final String encoded = qs.encode(testCase['data']);
final Map decoded = qs.decode(testCase['encoded']);
yield 'Encoded: $encoded';
yield 'Decoded: ${jsonEncode(decoded)}';
}
}
2 changes: 1 addition & 1 deletion test/comparison/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "A comparison of query string parsing libraries",
"author": "Klemen Tusar",
"license": "BSD-3-Clause",
"packageManager": "pnpm@11.1.3",
"packageManager": "pnpm@11.5.2",
"dependencies": {
"qs": "^6.15.2"
}
Expand Down
15 changes: 4 additions & 11 deletions test/comparison/qs.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:convert' show jsonDecode, jsonEncode;
import 'dart:io' show File, Platform, exitCode, stdout;

import 'package:cli_script/cli_script.dart' show wrapMain;
import 'package:path/path.dart' as p;
import 'package:qs_dart/qs_dart.dart' as qs;

import 'comparison.dart';

void main() {
wrapMain(() {
Expand All @@ -12,16 +12,9 @@ void main() {
final String scriptDir = p.dirname(Platform.script.toFilePath());
final File file = File(p.join(scriptDir, 'test_cases.json'));
final String contents = file.readAsStringSync();
final List<Map<String, dynamic>> e2eTestCases =
List<Map<String, dynamic>>.from(
jsonDecode(contents),
);

for (final testCase in e2eTestCases) {
final String encoded = qs.encode(testCase['data']);
final Map decoded = qs.decode(testCase['encoded']);
stdout.writeln('Encoded: $encoded');
stdout.writeln('Decoded: ${jsonEncode(decoded)}');
for (final String line in buildComparisonLines(contents)) {
stdout.writeln(line);
}
});
}
17 changes: 17 additions & 0 deletions test/comparison/qs_dart2js.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'dart:convert' show base64Decode, utf8;

import 'comparison.dart';

void main() {
const String encodedTestCases =
String.fromEnvironment('QS_COMPARISON_TEST_CASES_BASE64');
if (encodedTestCases.isEmpty) {
throw StateError('Missing QS_COMPARISON_TEST_CASES_BASE64.');
}

final String testCasesJson = utf8.decode(base64Decode(encodedTestCases));
for (final String line in buildComparisonLines(testCasesJson)) {
// ignore: avoid_print
print(line);
}
}