1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 15:27:35 +00:00

test-js: Display messages from console.log in test output

This will help greatly with debugging!
This commit is contained in:
Matthew Olsson 2020-07-04 20:23:46 -07:00 committed by Andreas Kling
parent 474159277f
commit a2dbd955f2
2 changed files with 51 additions and 14 deletions

View file

@ -6,6 +6,9 @@ let expect;
// name to avoid name collision. // name to avoid name collision.
let __TestResults__ = {}; let __TestResults__ = {};
// So test names like "toString" don't automatically produce an error
Object.setPrototypeOf(__TestResults__, null);
// This array is used to communicate with the C++ program. It treats // This array is used to communicate with the C++ program. It treats
// each message in this array as a separate message. Has a terrible // each message in this array as a separate message. Has a terrible
// name to avoid name collision. // name to avoid name collision.
@ -430,8 +433,12 @@ test = (message, callback) => {
__TestResults__[suiteMessage] = {}; __TestResults__[suiteMessage] = {};
const suite = __TestResults__[suiteMessage]; const suite = __TestResults__[suiteMessage];
if (suite[message]) if (suite[message]) {
throw new Error("Duplicate test name: " + message); suite[message] = {
result: "fail"
};
return;
}
try { try {
callback(); callback();

View file

@ -222,6 +222,7 @@ struct JSFileResult {
// precedence over a passed test // precedence over a passed test
TestResult most_severe_test_result { TestResult::Pass }; TestResult most_severe_test_result { TestResult::Pass };
Vector<JSSuite> suites {}; Vector<JSSuite> suites {};
Vector<String> logged_messages {};
}; };
struct JSTestRunnerCounts { struct JSTestRunnerCounts {
@ -333,12 +334,6 @@ JSFileResult TestRunner::run_file_test(const String& test_path)
return { test_path, file_program.error() }; return { test_path, file_program.error() };
interpreter->run(interpreter->global_object(), *file_program.value()); interpreter->run(interpreter->global_object(), *file_program.value());
// Print any output
// FIXME: Should be printed to stdout in a nice format
auto& arr = interpreter->get_variable("__UserOutput__", interpreter->global_object()).as_array();
for (auto& entry : arr.indexed_properties()) {
dbg() << test_path << ": " << entry.value_and_attributes(&interpreter->global_object()).value.to_string_without_side_effects();
}
auto test_json = get_test_results(*interpreter); auto test_json = get_test_results(*interpreter);
if (!test_json.has_value()) { if (!test_json.has_value()) {
@ -348,6 +343,13 @@ JSFileResult TestRunner::run_file_test(const String& test_path)
JSFileResult file_result { test_path }; JSFileResult file_result { test_path };
// Collect logged messages
auto& arr = interpreter->get_variable("__UserOutput__", interpreter->global_object()).as_array();
for (auto& entry : arr.indexed_properties()) {
auto message = entry.value_and_attributes(&interpreter->global_object()).value;
file_result.logged_messages.append(message.to_string_without_side_effects());
}
test_json.value().as_object().for_each_member([&](const String& suite_name, const JsonValue& suite_value) { test_json.value().as_object().for_each_member([&](const String& suite_name, const JsonValue& suite_value) {
JSSuite suite { suite_name }; JSSuite suite { suite_name };
@ -474,17 +476,35 @@ void TestRunner::print_file_result(const JSFileResult& file_result) const
printf("\n"); printf("\n");
} }
if (!file_result.logged_messages.is_empty()) {
print_modifiers({ FG_GRAY, FG_BOLD });
#ifdef __serenity__
printf(" Console output:\n");
#else
// This emoji has a second invisible byte after it. The one above does not
printf(" Console output:\n");
#endif
print_modifiers({ CLEAR, FG_GRAY });
for (auto& message : file_result.logged_messages)
printf(" %s\n", message.characters());
}
if (file_result.error.has_value()) { if (file_result.error.has_value()) {
auto test_error = file_result.error.value(); auto test_error = file_result.error.value();
print_modifiers({ FG_RED }); print_modifiers({ FG_RED });
printf(" ❌ The file failed to parse\n\n"); #ifdef __serenity__
printf(" ❌ The file failed to parse\n\n");
#else
// No invisible byte here, but the spacing still needs to be altered on the host
printf(" ❌ The file failed to parse\n\n");
#endif
print_modifiers({ FG_GRAY }); print_modifiers({ FG_GRAY });
for (auto& message : test_error.hint.split('\n', true)) { for (auto& message : test_error.hint.split('\n', true)) {
printf(" %s\n", message.characters()); printf(" %s\n", message.characters());
} }
print_modifiers({ FG_RED }); print_modifiers({ FG_RED });
printf(" %s\n\n", test_error.error.to_string().characters()); printf(" %s\n\n", test_error.error.to_string().characters());
return; return;
} }
@ -499,9 +519,19 @@ void TestRunner::print_file_result(const JSFileResult& file_result) const
print_modifiers({ FG_GRAY, FG_BOLD }); print_modifiers({ FG_GRAY, FG_BOLD });
if (failed) { if (failed) {
printf(" ❌ Suite: "); #ifdef __serenity__
printf(" ❌ Suite: ");
#else
// No invisible byte here, but the spacing still needs to be altered on the host
printf(" ❌ Suite: ");
#endif
} else { } else {
printf(" ⚠️️ Suite: "); #ifdef __serenity__
printf(" ⚠ Suite: ");
#else
// This emoji has a second invisible byte after it. The one above does not
printf(" ⚠️ Suite: ");
#endif
} }
print_modifiers({ CLEAR, FG_GRAY }); print_modifiers({ CLEAR, FG_GRAY });
@ -518,7 +548,7 @@ void TestRunner::print_file_result(const JSFileResult& file_result) const
continue; continue;
print_modifiers({ FG_GRAY, FG_BOLD }); print_modifiers({ FG_GRAY, FG_BOLD });
printf(" Test: "); printf(" Test: ");
if (test.result == TestResult::Fail) { if (test.result == TestResult::Fail) {
print_modifiers({ CLEAR, FG_RED }); print_modifiers({ CLEAR, FG_RED });
printf("%s (failed)\n", test.name.characters()); printf("%s (failed)\n", test.name.characters());