diff options
Diffstat (limited to 'devtools/client/webconsole/test')
352 files changed, 22823 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/.eslintrc.js b/devtools/client/webconsole/test/.eslintrc.js new file mode 100644 index 000000000..8d15a76d9 --- /dev/null +++ b/devtools/client/webconsole/test/.eslintrc.js @@ -0,0 +1,6 @@ +"use strict"; + +module.exports = { + // Extend from the shared list of defined globals for mochitests. + "extends": "../../../.eslintrc.mochitests.js" +}; diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini new file mode 100644 index 000000000..918411182 --- /dev/null +++ b/devtools/client/webconsole/test/browser.ini @@ -0,0 +1,396 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + head.js + test-bug-585956-console-trace.html + test-bug-593003-iframe-wrong-hud-iframe.html + test-bug-593003-iframe-wrong-hud.html + test-bug-595934-canvas-css.html + test-bug-595934-canvas-css.js + test-bug-595934-css-loader.css + test-bug-595934-css-loader.css^headers^ + test-bug-595934-css-loader.html + test-bug-595934-css-parser.css + test-bug-595934-css-parser.html + test-bug-595934-empty-getelementbyid.html + test-bug-595934-empty-getelementbyid.js + test-bug-595934-html.html + test-bug-595934-image.html + test-bug-595934-image.jpg + test-bug-595934-imagemap.html + test-bug-595934-malformedxml-external.html + test-bug-595934-malformedxml-external.xml + test-bug-595934-malformedxml.xhtml + test-bug-595934-svg.xhtml + test-bug-595934-workers.html + test-bug-595934-workers.js + test-bug-597136-external-script-errors.html + test-bug-597136-external-script-errors.js + test-bug-597756-reopen-closed-tab.html + test-bug-599725-response-headers.sjs + test-bug-600183-charset.html + test-bug-600183-charset.html^headers^ + test-bug-601177-log-levels.html + test-bug-601177-log-levels.js + test-bug-603750-websocket.html + test-bug-603750-websocket.js + test-bug-613013-console-api-iframe.html + test-bug-618078-network-exceptions.html + test-bug-621644-jsterm-dollar.html + test-bug-630733-response-redirect-headers.sjs + test-bug-632275-getters.html + test-bug-632347-iterators-generators.html + test-bug-644419-log-limits.html + test-bug-646025-console-file-location.html + test-bug-658368-time-methods.html + test-bug-737873-mixedcontent.html + test-bug-752559-ineffective-iframe-sandbox-warning0.html + test-bug-752559-ineffective-iframe-sandbox-warning1.html + test-bug-752559-ineffective-iframe-sandbox-warning2.html + test-bug-752559-ineffective-iframe-sandbox-warning3.html + test-bug-752559-ineffective-iframe-sandbox-warning4.html + test-bug-752559-ineffective-iframe-sandbox-warning5.html + test-bug-752559-ineffective-iframe-sandbox-warning-inner.html + test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html + test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html + test-bug-762593-insecure-passwords-about-blank-web-console-warning.html + test-bug-762593-insecure-passwords-web-console-warning.html + test-bug-766001-console-log.js + test-bug-766001-js-console-links.html + test-bug-766001-js-errors.js + test-bug-782653-css-errors-1.css + test-bug-782653-css-errors-2.css + test-bug-782653-css-errors.html + test-bug-837351-security-errors.html + test-bug-859170-longstring-hang.html + test-bug-869003-iframe.html + test-bug-869003-top-window.html + test-closure-optimized-out.html + test-closures.html + test-console-assert.html + test-console-clear.html + test-console-count.html + test-console-count-external-file.js + test-console-extras.html + test-console-replaced-api.html + test-console-server-logging.sjs + test-console-server-logging-array.sjs + test-console.html + test-console-workers.html + test-console-table.html + test-console-output-02.html + test-console-output-03.html + test-console-output-04.html + test-console-output-dom-elements.html + test-console-output-events.html + test-console-output-regexp.html + test-console-column.html + test-consoleiframes.html + test-console-trace-async.html + test-certificate-messages.html + test-cu-reporterror.js + test-data.json + test-data.json^headers^ + test-duplicate-error.html + test-encoding-ISO-8859-1.html + test-error.html + test-eval-in-stackframe.html + test-file-location.js + test-filter.html + test-for-of.html + test_hpkp-invalid-headers.sjs + test_hsts-invalid-headers.sjs + test-iframe-762593-insecure-form-action.html + test-iframe-762593-insecure-frame.html + test-iframe1.html + test-iframe2.html + test-iframe3.html + test-image.png + test-mixedcontent-securityerrors.html + test-mutation.html + test-network-request.html + test-network.html + test-observe-http-ajax.html + test-own-console.html + test-property-provider.html + test-repeated-messages.html + test-result-format-as-string.html + test-trackingprotection-securityerrors.html + test-webconsole-error-observer.html + test_bug_770099_violation.html + test_bug_770099_violation.html^headers^ + test-autocomplete-in-stackframe.html + testscript.js + test-bug_923281_console_log_filter.html + test-bug_923281_test1.js + test-bug_923281_test2.js + test-bug_939783_console_trace_duplicates.html + test-bug-952277-highlight-nodes-in-vview.html + test-bug-609872-cd-iframe-parent.html + test-bug-609872-cd-iframe-child.html + test-bug-989025-iframe-parent.html + test-bug_1050691_click_function_to_source.html + test-bug_1050691_click_function_to_source.js + test-console-api-stackframe.html + test-exception-stackframe.html + test_bug_1010953_cspro.html^headers^ + test_bug_1010953_cspro.html + test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^ + test_bug1045902_console_csp_ignore_reflected_xss_message.html + test_bug1092055_shouldwarn.js^headers^ + test_bug1092055_shouldwarn.js + test_bug1092055_shouldwarn.html + test_bug_1247459_violation.html + !/devtools/client/framework/test/shared-head.js + !/devtools/client/netmonitor/test/sjs_cors-test-server.sjs + !/image/test/mochitest/blue.png + +[browser_bug1045902_console_csp_ignore_reflected_xss_message.js] +skip-if = (e10s && debug) || (e10s && os == 'win') # Bug 1221499 enabled these on windows +[browser_bug664688_sandbox_update_after_navigation.js] +[browser_bug_638949_copy_link_location.js] +subsuite = clipboard +[browser_bug_862916_console_dir_and_filter_off.js] +skip-if = (e10s && (os == 'win' || os == 'mac')) # Bug 1243976 +[browser_bug_865288_repeat_different_objects.js] +[browser_bug_865871_variables_view_close_on_esc_key.js] +[browser_bug_869003_inspect_cross_domain_object.js] +[browser_bug_871156_ctrlw_close_tab.js] +[browser_cached_messages.js] +[browser_console.js] +[browser_console_addonsdk_loader_exception.js] +[browser_console_clear_method.js] +[browser_console_clear_on_reload.js] +[browser_console_click_focus.js] +[browser_console_consolejsm_output.js] +[browser_console_copy_command.js] +subsuite = clipboard +[browser_console_dead_objects.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_console_copy_entire_message_context_menu.js] +subsuite = clipboard +[browser_console_error_source_click.js] +[browser_console_filters.js] +[browser_console_iframe_messages.js] +[browser_console_keyboard_accessibility.js] +[browser_console_log_inspectable_object.js] +[browser_console_native_getters.js] +[browser_console_navigation_marker.js] +[browser_console_netlogging.js] +[browser_console_nsiconsolemessage.js] +[browser_console_optimized_out_vars.js] +[browser_console_private_browsing.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests +[browser_console_server_logging.js] +[browser_console_variables_view.js] +[browser_console_variables_view_filter.js] +[browser_console_variables_view_dom_nodes.js] +[browser_console_variables_view_dont_sort_non_sortable_classes_properties.js] +[browser_console_variables_view_special_names.js] +[browser_console_variables_view_while_debugging.js] +[browser_console_variables_view_while_debugging_and_inspecting.js] +[browser_eval_in_debugger_stackframe.js] +[browser_eval_in_debugger_stackframe2.js] +[browser_jsterm_inspect.js] +skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1243966 +[browser_longstring_hang.js] +[browser_output_breaks_after_console_dir_uninspectable.js] +[browser_output_longstring_expand.js] +[browser_repeated_messages_accuracy.js] +[browser_result_format_as_string.js] +[browser_warn_user_about_replaced_api.js] +[browser_webconsole_allow_mixedcontent_securityerrors.js] +tags = mcb +[browser_webconsole_script_errordoc_urls.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_assert.js] +[browser_webconsole_block_mixedcontent_securityerrors.js] +tags = mcb +[browser_webconsole_bug_579412_input_focus.js] +[browser_webconsole_bug_580001_closing_after_completion.js] +[browser_webconsole_bug_580030_errors_after_page_reload.js] +[browser_webconsole_bug_580454_timestamp_l10n.js] +[browser_webconsole_bug_582201_duplicate_errors.js] +[browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js] +[browser_webconsole_bug_585237_line_limit.js] +[browser_webconsole_bug_585956_console_trace.js] +[browser_webconsole_bug_585991_autocomplete_keys.js] +[browser_webconsole_bug_585991_autocomplete_popup.js] +[browser_webconsole_bug_586388_select_all.js] +[browser_webconsole_bug_587617_output_copy.js] +subsuite = clipboard +[browser_webconsole_bug_588342_document_focus.js] +[browser_webconsole_bug_588730_text_node_insertion.js] +[browser_webconsole_bug_588967_input_expansion.js] +[browser_webconsole_bug_589162_css_filter.js] +[browser_webconsole_bug_592442_closing_brackets.js] +[browser_webconsole_bug_593003_iframe_wrong_hud.js] +[browser_webconsole_bug_594497_history_arrow_keys.js] +[browser_webconsole_bug_595223_file_uri.js] +[browser_webconsole_bug_595350_multiple_windows_and_tabs.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_bug_595934_message_categories.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js] +[browser_webconsole_bug_597136_external_script_errors.js] +[browser_webconsole_bug_597136_network_requests_from_chrome.js] +[browser_webconsole_bug_597460_filter_scroll.js] +[browser_webconsole_bug_597756_reopen_closed_tab.js] +[browser_webconsole_bug_599725_response_headers.js] +[browser_webconsole_bug_600183_charset.js] +[browser_webconsole_bug_601177_log_levels.js] +[browser_webconsole_bug_601352_scroll.js] +[browser_webconsole_bug_601667_filter_buttons.js] +[browser_webconsole_bug_603750_websocket.js] +[browser_webconsole_bug_611795.js] +[browser_webconsole_bug_613013_console_api_iframe.js] +[browser_webconsole_bug_613280_jsterm_copy.js] +subsuite = clipboard +[browser_webconsole_bug_613642_maintain_scroll.js] +[browser_webconsole_bug_613642_prune_scroll.js] +[browser_webconsole_bug_614793_jsterm_scroll.js] +[browser_webconsole_bug_618078_network_exceptions.js] +[browser_webconsole_bug_621644_jsterm_dollar.js] +[browser_webconsole_bug_622303_persistent_filters.js] +[browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js] +skip-if = os != "win" +[browser_webconsole_bug_630733_response_redirect_headers.js] +[browser_webconsole_bug_632275_getters_document_width.js] +[browser_webconsole_bug_632347_iterators_generators.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_bug_632817.js] +skip-if = true # Bug 1244707 +[browser_webconsole_bug_642108_pruneTest.js] +[browser_webconsole_autocomplete_and_selfxss.js] +subsuite = clipboard +[browser_webconsole_bug_644419_log_limits.js] +[browser_webconsole_bug_646025_console_file_location.js] +[browser_webconsole_bug_651501_document_body_autocomplete.js] +[browser_webconsole_bug_653531_highlighter_console_helper.js] +skip-if = true # Requires direct access to content nodes +[browser_webconsole_bug_658368_time_methods.js] +[browser_webconsole_bug_659907_console_dir.js] +[browser_webconsole_bug_660806_history_nav.js] +[browser_webconsole_bug_664131_console_group.js] +[browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js] +[browser_webconsole_bug_704295.js] +[browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js] +[browser_webconsole_bug_737873_mixedcontent.js] +tags = mcb +[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js] +[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js] +[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js] +skip-if = true # Bug 1110500 - mouse event failure in test +[browser_webconsole_bug_764572_output_open_url.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_bug_766001_JS_Console_in_Debugger.js] +[browser_webconsole_bug_770099_violation.js] +skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243978 +[browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js] +[browser_webconsole_bug_804845_ctrl_key_nav.js] +skip-if = os != "mac" +[browser_webconsole_bug_817834_add_edited_input_to_history.js] +[browser_webconsole_bug_837351_securityerrors.js] +[browser_webconsole_filter_buttons_contextmenu.js] +[browser_webconsole_bug_1006027_message_timestamps_incorrect.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent) +[browser_webconsole_bug_1010953_cspro.js] +skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243967 +[browser_webconsole_bug_1247459_violation.js] +skip-if = e10s && (os == 'win') # Bug 1264955 +[browser_webconsole_certificate_messages.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_show_subresource_security_errors.js] +skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243987 +[browser_webconsole_cached_autocomplete.js] +[browser_webconsole_chrome.js] +[browser_webconsole_clear_method.js] +[browser_webconsole_clickable_urls.js] +[browser_webconsole_closure_inspection.js] +[browser_webconsole_completion.js] +[browser_webconsole_console_extras.js] +[browser_webconsole_console_logging_api.js] +[browser_webconsole_console_logging_workers_api.js] +[browser_webconsole_console_trace_async.js] +[browser_webconsole_count.js] +[browser_webconsole_dont_navigate_on_doubleclick.js] +[browser_webconsole_execution_scope.js] +[browser_webconsole_for_of.js] +[browser_webconsole_history.js] +[browser_webconsole_hpkp_invalid-headers.js] +[browser_webconsole_hsts_invalid-headers.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests +[browser_webconsole_input_field_focus_on_panel_select.js] +[browser_webconsole_inspect-parsed-documents.js] +[browser_webconsole_js_input_expansion.js] +[browser_webconsole_jsterm.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout) +[browser_webconsole_live_filtering_of_message_types.js] +[browser_webconsole_live_filtering_on_search_strings.js] +[browser_webconsole_message_node_id.js] +[browser_webconsole_multiline_input.js] +[browser_webconsole_netlogging.js] +skip-if = true # Bug 1298364 +[browser_webconsole_netlogging_basic.js] +[browser_webconsole_netlogging_panel.js] +[browser_webconsole_netlogging_reset_filter.js] +[browser_webconsole_notifications.js] +[browser_webconsole_open-links-without-callback.js] +[browser_webconsole_promise.js] +[browser_webconsole_output_copy_newlines.js] +subsuite = clipboard +[browser_webconsole_output_order.js] +[browser_webconsole_property_provider.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s +[browser_webconsole_scratchpad_panel_link.js] +[browser_webconsole_split.js] +[browser_webconsole_split_escape_key.js] +[browser_webconsole_split_focus.js] +[browser_webconsole_split_persist.js] +[browser_webconsole_trackingprotection_errors.js] +tags = trackingprotection +[browser_webconsole_view_source.js] +[browser_webconsole_reflow.js] +[browser_webconsole_log_file_filter.js] +[browser_webconsole_expandable_timestamps.js] +[browser_webconsole_autocomplete_accessibility.js] +[browser_webconsole_autocomplete_in_debugger_stackframe.js] +[browser_webconsole_autocomplete_popup_close_on_tab_switch.js] +[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js] +[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js] +[browser_console_history_persist.js] +[browser_webconsole_output_01.js] +[browser_webconsole_output_02.js] +[browser_webconsole_output_03.js] +[browser_webconsole_output_04.js] +[browser_webconsole_output_05.js] +[browser_webconsole_output_06.js] +[browser_webconsole_output_dom_elements_01.js] +[browser_webconsole_output_dom_elements_02.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout) +[browser_webconsole_output_dom_elements_03.js] +skip-if = e10s # Bug 1241019 +[browser_webconsole_output_dom_elements_04.js] +skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout) +[browser_webconsole_output_dom_elements_05.js] +[browser_webconsole_output_events.js] +[browser_webconsole_output_regexp.js] +[browser_webconsole_output_table.js] +[browser_console_variables_view_highlighter.js] +[browser_webconsole_start_netmon_first.js] +[browser_webconsole_console_trace_duplicates.js] +[browser_webconsole_cd_iframe.js] +[browser_webconsole_autocomplete_crossdomain_iframe.js] +[browser_webconsole_console_custom_styles.js] +[browser_webconsole_console_api_stackframe.js] +[browser_webconsole_exception_stackframe.js] +[browser_webconsole_column_numbers.js] +[browser_console_open_or_focus.js] +[browser_webconsole_bug_922212_console_dirxml.js] +[browser_webconsole_shows_reqs_in_netmonitor.js] +[browser_netmonitor_shows_reqs_in_webconsole.js] +[browser_webconsole_bug_1050691_click_function_to_source.js] +[browser_webconsole_context_menu_open_in_var_view.js] +[browser_webconsole_context_menu_store_as_global.js] +[browser_webconsole_strict_mode_errors.js] diff --git a/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js b/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js new file mode 100644 index 000000000..cfbf61795 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that a file with an unsupported CSP directive ('reflected-xss filter') +// displays the appropriate message to the console. + +"use strict"; + +const EXPECTED_RESULT = "Not supporting directive \u2018reflected-xss\u2019. " + + "Directive and values will be ignored."; +const TEST_FILE = "http://example.com/browser/devtools/client/webconsole/" + + "test/test_bug1045902_console_csp_ignore_reflected_xss_" + + "message.html"; + +var hud = undefined; + +var TEST_URI = "data:text/html;charset=utf8,Web Console CSP ignoring " + + "reflected XSS (bug 1045902)"; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + hud = yield openConsole(); + + yield loadDocument(browser); + yield testViolationMessage(); + + hud = null; +}); + +function loadDocument(browser) { + hud.jsterm.clearOutput(); + browser.loadURI(TEST_FILE); + return BrowserTestUtils.browserLoaded(browser); +} + +function testViolationMessage() { + let aOutputNode = hud.outputNode; + + return waitForSuccess({ + name: "Confirming that CSP logs messages to the console when " + + "\u2018reflected-xss\u2019 directive is used!", + validator: function () { + console.log(aOutputNode.textContent); + let success = false; + success = aOutputNode.textContent.indexOf(EXPECTED_RESULT) > -1; + return success; + } + }); +} diff --git a/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js b/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js new file mode 100644 index 000000000..1aacb61c1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js @@ -0,0 +1,92 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests if the JSTerm sandbox is updated when the user navigates from one +// domain to another, in order to avoid permission denied errors with a sandbox +// created for a different origin. + +"use strict"; + +add_task(function* () { + const TEST_URI1 = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + const TEST_URI2 = "http://example.org/browser/devtools/client/webconsole/" + + "test/test-console.html"; + + yield loadTab(TEST_URI1); + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + hud.jsterm.execute("window.location.href"); + + info("wait for window.location.href"); + + let msgForLocation1 = { + webconsole: hud, + messages: [ + { + name: "window.location.href jsterm input", + text: "window.location.href", + category: CATEGORY_INPUT, + }, + { + name: "window.location.href result is displayed", + text: TEST_URI1, + category: CATEGORY_OUTPUT, + }, + ], + }; + + yield waitForMessages(msgForLocation1); + + // load second url + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2); + yield loadBrowser(gBrowser.selectedBrowser); + + is(hud.outputNode.textContent.indexOf("Permission denied"), -1, + "no permission denied errors"); + + hud.jsterm.clearOutput(); + hud.jsterm.execute("window.location.href"); + + info("wait for window.location.href after page navigation"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "window.location.href jsterm input", + text: "window.location.href", + category: CATEGORY_INPUT, + }, + { + name: "window.location.href result is displayed", + text: TEST_URI2, + category: CATEGORY_OUTPUT, + }, + ], + }); + + is(hud.outputNode.textContent.indexOf("Permission denied"), -1, + "no permission denied errors"); + + // Navigation clears messages. Wait for that clear to happen before + // continuing the test or it might destroy messages we wait later on (Bug + // 1270234). + let cleared = hud.jsterm.once("messages-cleared"); + + gBrowser.goBack(); + + info("Waiting for messages to be cleared due to navigation"); + yield cleared; + + info("Messages cleared after navigation; checking location"); + hud.jsterm.execute("window.location.href"); + + info("wait for window.location.href after goBack()"); + yield waitForMessages(msgForLocation1); + is(hud.outputNode.textContent.indexOf("Permission denied"), -1, + "no permission denied errors"); +}); diff --git a/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js b/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js new file mode 100644 index 000000000..54bdbe499 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js @@ -0,0 +1,107 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test for the "Copy link location" context menu item shown when you right +// click network requests in the output. + +"use strict"; + +add_task(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html?_date=" + Date.now(); + const COMMAND_NAME = "consoleCmd_copyURL"; + const CONTEXT_MENU_ID = "#menu_copyURL"; + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.webconsole.filter.networkinfo"); + }); + + Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true); + + yield loadTab(TEST_URI); + let hud = yield openConsole(); + let output = hud.outputNode; + let menu = hud.iframeWindow.document.getElementById("output-contextmenu"); + + hud.jsterm.clearOutput(); + content.console.log("bug 638949"); + + // Test that the "Copy Link Location" command is disabled for non-network + // messages. + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug 638949", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + output.focus(); + let message = [...result.matched][0]; + + goUpdateCommand(COMMAND_NAME); + ok(!isEnabled(), COMMAND_NAME + " is disabled"); + + // Test that the "Copy Link Location" menu item is hidden for non-network + // messages. + yield waitForContextMenu(menu, message, () => { + let isHidden = menu.querySelector(CONTEXT_MENU_ID).hidden; + ok(isHidden, CONTEXT_MENU_ID + " is hidden"); + }); + + hud.jsterm.clearOutput(); + // Reloading will produce network logging + content.location.reload(); + + // Test that the "Copy Link Location" command is enabled and works + // as expected for any network-related message. + // This command should copy only the URL. + [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test-console.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); + + output.focus(); + message = [...result.matched][0]; + hud.ui.output.selectMessage(message); + + goUpdateCommand(COMMAND_NAME); + ok(isEnabled(), COMMAND_NAME + " is enabled"); + + info("expected clipboard value: " + message.url); + + let deferred = promise.defer(); + + waitForClipboard((aData) => { + return aData.trim() == message.url; + }, () => { + goDoCommand(COMMAND_NAME); + }, () => { + deferred.resolve(null); + }, () => { + deferred.reject(null); + }); + + yield deferred.promise; + + // Test that the "Copy Link Location" menu item is visible for network-related + // messages. + yield waitForContextMenu(menu, message, () => { + let isVisible = !menu.querySelector(CONTEXT_MENU_ID).hidden; + ok(isVisible, CONTEXT_MENU_ID + " is visible"); + }); + + // Return whether "Copy Link Location" command is enabled or not. + function isEnabled() { + let controller = top.document.commandDispatcher + .getControllerForCommand(COMMAND_NAME); + return controller && controller.isCommandEnabled(COMMAND_NAME); + } +}); diff --git a/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js b/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js new file mode 100644 index 000000000..9d04076ee --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js @@ -0,0 +1,31 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the output for console.dir() works even if Logging filter is off. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 862916"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + ok(hud, "web console opened"); + + hud.setFilterState("log", false); + registerCleanupFunction(() => hud.setFilterState("log", true)); + + hud.jsterm.execute("window.fooBarz = 'bug862916'; " + + "console.dir(window)"); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + ok(varView, "variables view object"); + + yield findVariableViewProperties(varView, [ + { name: "fooBarz", value: "bug862916" }, + ], { webconsole: hud }); +}); + diff --git a/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js b/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js new file mode 100644 index 000000000..86ab5bd39 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure messages are not considered repeated when console.log() +// is invoked with different objects, see bug 865288. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-repeated-messages.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + info("waiting for 3 console.log objects"); + + hud.jsterm.clearOutput(true); + hud.jsterm.execute("window.testConsoleObjects()"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "3 console.log messages", + text: "abba", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 3, + repeats: 1, + objects: true, + }], + }); + + let msgs = [...result.matched]; + is(msgs.length, 3, "3 message elements"); + + for (let i = 0; i < msgs.length; i++) { + info("test message element #" + i); + + let msg = msgs[i]; + let clickable = msg.querySelector(".message-body a"); + ok(clickable, "clickable object #" + i); + + msg.scrollIntoView(false); + yield clickObject(clickable, i); + } + + function* clickObject(obj, i) { + executeSoon(() => { + EventUtils.synthesizeMouse(obj, 2, 2, {}, hud.iframeWindow); + }); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + ok(varView, "variables view fetched #" + i); + + yield findVariableViewProperties(varView, [ + { name: "id", value: "abba" + i }, + ], { webconsole: hud }); + } +}); + diff --git a/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js b/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js new file mode 100644 index 000000000..044525b28 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js @@ -0,0 +1,75 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the variables view sidebar can be closed by pressing Escape in the +// web console. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +function test() { + let hud; + + Task.spawn(runner).then(finishTest); + + function* runner() { + let {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + let jsterm = hud.jsterm; + let result; + let vview; + let msg; + + yield openSidebar("fooObj", + 'testProp: "testValue"', + { name: "testProp", value: "testValue" }); + + let prop = result.matchedProp; + ok(prop, "matched the |testProp| property in the variables view"); + + vview.window.focus(); + + let sidebarClosed = jsterm.once("sidebar-closed"); + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield sidebarClosed; + + jsterm.clearOutput(); + + yield openSidebar("window.location", + "Location \u2192 http://example.com/browser/", + { name: "host", value: "example.com" }); + + vview.window.focus(); + + msg.scrollIntoView(); + sidebarClosed = jsterm.once("sidebar-closed"); + EventUtils.synthesizeKey("VK_ESCAPE", {}); + yield sidebarClosed; + + function* openSidebar(objName, expectedText, expectedObj) { + msg = yield jsterm.execute(objName); + ok(msg, "output message found"); + + let anchor = msg.querySelector("a"); + let body = msg.querySelector(".message-body"); + ok(anchor, "object anchor"); + ok(body, "message body"); + ok(body.textContent.includes(expectedText), "message text check"); + + msg.scrollIntoView(); + yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow); + + let vviewVar = yield jsterm.once("variablesview-fetched"); + vview = vviewVar._variablesView; + ok(vview, "variables view object exists"); + + [result] = yield findVariableViewProperties(vviewVar, [ + expectedObj, + ], { webconsole: hud }); + } + } +} diff --git a/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js b/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js new file mode 100644 index 000000000..685148fc7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that users can inspect objects logged from cross-domain iframes - +// bug 869003. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-869003-top-window.html"; + +add_task(function* () { + // This test is slightly more involved: it opens the web console, then the + // variables view for a given object, it updates a property in the view and + // checks the result. We can get a timeout with debug builds on slower + // machines. + requestLongerTimeout(2); + + yield loadTab("data:text/html;charset=utf8,<p>hello"); + let hud = yield openConsole(); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log message", + text: "foobar", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + objects: true, + }], + }); + + let msg = [...result.matched][0]; + ok(msg, "message element"); + + let body = msg.querySelector(".message-body"); + ok(body, "message body"); + + let clickable = result.clickableElements[0]; + ok(clickable, "clickable object found"); + ok(body.textContent.includes('{ hello: "world!",'), "message text check"); + + executeSoon(() => { + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + }); + + let aVar = yield hud.jsterm.once("variablesview-fetched"); + ok(aVar, "variables view fetched"); + ok(aVar._variablesView, "variables view object"); + + [result] = yield findVariableViewProperties(aVar, [ + { name: "hello", value: "world!" }, + { name: "bug", value: 869003 }, + ], { webconsole: hud }); + + let prop = result.matchedProp; + ok(prop, "matched the |hello| property in the variables view"); + + // Check that property value updates work. + aVar = yield updateVariablesViewProperty({ + property: prop, + field: "value", + string: "'omgtest'", + webconsole: hud, + }); + + info("onFetchAfterUpdate"); + + yield findVariableViewProperties(aVar, [ + { name: "hello", value: "omgtest" }, + { name: "bug", value: 869003 }, + ], { webconsole: hud }); +}); diff --git a/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js b/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js new file mode 100644 index 000000000..c1698cf91 --- /dev/null +++ b/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js @@ -0,0 +1,78 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that Ctrl-W closes the Browser Console and that Ctrl-W closes the +// current tab when using the Web Console - bug 871156. + +"use strict"; + +add_task(function* () { + const TEST_URI = "data:text/html;charset=utf8,<title>bug871156</title>\n" + + "<p>hello world"; + let firstTab = gBrowser.selectedTab; + + Services.prefs.setBoolPref("browser.tabs.animate", false); + registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.tabs.animate"); + }); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + ok(hud, "Web Console opened"); + + let tabClosed = promise.defer(); + let toolboxDestroyed = promise.defer(); + let tabSelected = promise.defer(); + + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + + gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() { + gBrowser.tabContainer.removeEventListener("TabClose", onTabClose); + info("tab closed"); + tabClosed.resolve(null); + }); + + gBrowser.tabContainer.addEventListener("TabSelect", function onTabSelect() { + gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect); + if (gBrowser.selectedTab == firstTab) { + info("tab selected"); + tabSelected.resolve(null); + } + }); + + toolbox.once("destroyed", () => { + info("toolbox destroyed"); + toolboxDestroyed.resolve(null); + }); + + // Get out of the web console initialization. + executeSoon(() => { + EventUtils.synthesizeKey("w", { accelKey: true }); + }); + + yield promise.all([tabClosed.promise, toolboxDestroyed.promise, + tabSelected.promise]); + info("promise.all resolved. start testing the Browser Console"); + + hud = yield HUDService.toggleBrowserConsole(); + ok(hud, "Browser Console opened"); + + let deferred = promise.defer(); + + Services.obs.addObserver(function onDestroy() { + Services.obs.removeObserver(onDestroy, "web-console-destroyed"); + ok(true, "the Browser Console closed"); + + deferred.resolve(null); + }, "web-console-destroyed", false); + + waitForFocus(() => { + EventUtils.synthesizeKey("w", { accelKey: true }, hud.iframeWindow); + }, hud.iframeWindow); + + yield deferred.promise; +}); diff --git a/devtools/client/webconsole/test/browser_cached_messages.js b/devtools/client/webconsole/test/browser_cached_messages.js new file mode 100644 index 000000000..bf69deee3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_cached_messages.js @@ -0,0 +1,59 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test to see if the cached messages are displayed when the console UI is +// opened. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-webconsole-error-observer.html"; + +// On e10s, the exception is triggered in child process +// and is ignored by test harness +if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); +} + +function test() { + waitForExplicitFinish(); + + loadTab(TEST_URI).then(testOpenUI); +} + +function testOpenUI(aTestReopen) { + openConsole().then((hud) => { + waitForMessages({ + webconsole: hud, + messages: [ + { + text: "log Bazzle", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "error Bazzle", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + }, + { + text: "bazBug611032", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "cssColorBug611032", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }, + ], + }).then(() => { + closeConsole(gBrowser.selectedTab).then(() => { + aTestReopen && info("will reopen the Web Console"); + executeSoon(aTestReopen ? testOpenUI : finishTest); + }); + }); + }); +} diff --git a/devtools/client/webconsole/test/browser_console.js b/devtools/client/webconsole/test/browser_console.js new file mode 100644 index 000000000..7bd1ffdc2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console.js @@ -0,0 +1,160 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the basic features of the Browser Console, bug 587757. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html?" + Date.now(); +const TEST_FILE = "chrome://mochitests/content/browser/devtools/client/" + + "webconsole/test/test-cu-reporterror.js"; + +const TEST_XHR_ERROR_URI = `http://example.com/404.html?${Date.now()}`; + +const TEST_IMAGE = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-image.png"; + +"use strict"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let opened = waitForConsole(); + + let hud = HUDService.getBrowserConsole(); + ok(!hud, "browser console is not open"); + info("wait for the browser console to open with ctrl-shift-j"); + EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window); + + hud = yield opened; + ok(hud, "browser console opened"); + + yield consoleOpened(hud); +}); + +function consoleOpened(hud) { + hud.jsterm.clearOutput(true); + + expectUncaughtException(); + executeSoon(() => { + foobarExceptionBug587757(); + }); + + // Add a message from a chrome window. + hud.iframeWindow.console.log("bug587757a"); + + // Check Cu.reportError stack. + // Use another js script to not depend on the test file line numbers. + Services.scriptloader.loadSubScript(TEST_FILE, hud.iframeWindow); + + // Add a message from a content window. + content.console.log("bug587757b"); + + // Test eval. + hud.jsterm.execute("document.location.href"); + + // Check for network requests. + let xhr = new XMLHttpRequest(); + xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status); + xhr.open("get", TEST_URI, true); + xhr.send(); + + // Check for xhr error. + let xhrErr = new XMLHttpRequest(); + xhrErr.onload = () => { + console.log("xhr error loaded, status is: " + xhrErr.status); + }; + xhrErr.open("get", TEST_XHR_ERROR_URI, true); + xhrErr.send(); + + // Check that Fetch requests are categorized as "XHR". + fetch(TEST_IMAGE).then(() => { console.log("fetch loaded"); }); + + return waitForMessages({ + webconsole: hud, + messages: [ + { + name: "chrome window console.log() is displayed", + text: "bug587757a", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "Cu.reportError is displayed", + text: "bug1141222", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + stacktrace: [{ + file: TEST_FILE, + line: 2, + }, { + file: TEST_FILE, + line: 4, + }, + // Ignore the rest of the stack, + // just assert Cu.reportError call site + // and consoleOpened call + ] + }, + { + name: "content window console.log() is displayed", + text: "bug587757b", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "jsterm eval result", + text: "browser.xul", + category: CATEGORY_OUTPUT, + severity: SEVERITY_LOG, + }, + { + name: "exception message", + text: "foobarExceptionBug587757", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + name: "network message", + text: "test-console.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_INFO, + isXhr: true, + }, + { + name: "xhr error message", + text: "404.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_ERROR, + isXhr: true, + }, + { + name: "network message", + text: "test-image.png", + category: CATEGORY_NETWORK, + severity: SEVERITY_INFO, + isXhr: true, + }, + ], + }); +} + +function waitForConsole() { + let deferred = promise.defer(); + + Services.obs.addObserver(function observer(aSubject) { + Services.obs.removeObserver(observer, "web-console-created"); + aSubject.QueryInterface(Ci.nsISupportsString); + + let hud = HUDService.getBrowserConsole(); + ok(hud, "browser console is open"); + is(aSubject.data, hud.hudId, "notification hudId is correct"); + + executeSoon(() => deferred.resolve(hud)); + }, "web-console-created", false); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js new file mode 100644 index 000000000..3eec65de3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js @@ -0,0 +1,92 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that exceptions from scripts loaded with the addon-sdk loader are +// opened correctly in View Source from the Browser Console. +// See bug 866950. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>hello world from bug 866950"; + +function test() { + requestLongerTimeout(2); + + let webconsole, browserconsole; + + Task.spawn(runner).then(finishTest); + + function* runner() { + let {tab} = yield loadTab(TEST_URI); + webconsole = yield openConsole(tab); + ok(webconsole, "web console opened"); + + browserconsole = yield HUDService.toggleBrowserConsole(); + ok(browserconsole, "browser console opened"); + + // Cause an exception in a script loaded with the addon-sdk loader. + let toolbox = gDevTools.getToolbox(webconsole.target); + let oldPanels = toolbox._toolPanels; + // non-iterable + toolbox._toolPanels = {}; + + function fixToolbox() { + toolbox._toolPanels = oldPanels; + } + + info("generate exception and wait for message"); + + executeSoon(() => { + executeSoon(fixToolbox); + expectUncaughtException(); + toolbox.getToolPanels(); + }); + + let [result] = yield waitForMessages({ + webconsole: browserconsole, + messages: [{ + text: "TypeError: this._toolPanels is not iterable", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); + + fixToolbox(); + + let msg = [...result.matched][0]; + ok(msg, "message element found"); + let locationNode = msg + .querySelector(".message .message-location > .frame-link"); + ok(locationNode, "message location element found"); + + let url = locationNode.getAttribute("data-url"); + info("location node url: " + url); + ok(url.indexOf("resource://") === 0, "error comes from a subscript"); + + let viewSource = browserconsole.viewSource; + let URL = null; + let clickPromise = promise.defer(); + browserconsole.viewSourceInDebugger = (sourceURL) => { + info("browserconsole.viewSourceInDebugger() was invoked: " + sourceURL); + URL = sourceURL; + clickPromise.resolve(null); + }; + + msg.scrollIntoView(); + EventUtils.synthesizeMouse(locationNode, 2, 2, {}, + browserconsole.iframeWindow); + + info("wait for click on locationNode"); + yield clickPromise.promise; + + info("view-source url: " + URL); + ok(URL, "we have some source URL after the click"); + isnot(URL.indexOf("toolbox.js"), -1, + "we have the expected view source URL"); + is(URL.indexOf("->"), -1, "no -> in the URL given to view-source"); + + browserconsole.viewSourceInDebugger = viewSource; + } +} diff --git a/devtools/client/webconsole/test/browser_console_clear_method.js b/devtools/client/webconsole/test/browser_console_clear_method.js new file mode 100644 index 000000000..33b43850e --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_clear_method.js @@ -0,0 +1,41 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that console.clear() does not clear the output of the browser console. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>Bug 1296870"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield HUDService.toggleBrowserConsole(); + + info("Log a new message from the content page"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.console.log("msg"); + }); + yield waitForMessage("msg", hud); + + info("Send a console.clear() from the content page"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.console.clear(); + }); + yield waitForMessage("Console was cleared", hud); + + info("Check that the messages logged after the first clear are still displayed"); + isnot(hud.outputNode.textContent.indexOf("msg"), -1, "msg is in the output"); +}); + +function waitForMessage(message, webconsole) { + return waitForMessages({ + webconsole, + messages: [{ + text: message, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} diff --git a/devtools/client/webconsole/test/browser_console_clear_on_reload.js b/devtools/client/webconsole/test/browser_console_clear_on_reload.js new file mode 100644 index 000000000..223eb028d --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_clear_on_reload.js @@ -0,0 +1,86 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that clear output on page reload works - bug 705921. +// Check that clear output and page reload remove the sidebar - bug 971967. + +"use strict"; + +add_task(function* () { + const PREF = "devtools.webconsole.persistlog"; + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + + Services.prefs.setBoolPref(PREF, false); + registerCleanupFunction(() => Services.prefs.clearUserPref(PREF)); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + ok(hud, "Web Console opened"); + + yield openSidebar("fooObj", { name: "testProp", value: "testValue" }); + + let sidebarClosed = hud.jsterm.once("sidebar-closed"); + hud.jsterm.clearOutput(); + yield sidebarClosed; + + hud.jsterm.execute("console.log('foobarz1')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarz1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + yield openSidebar("fooObj", { name: "testProp", value: "testValue" }); + + BrowserReload(); + + sidebarClosed = hud.jsterm.once("sidebar-closed"); + loadBrowser(gBrowser.selectedBrowser); + yield sidebarClosed; + + hud.jsterm.execute("console.log('foobarz2')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test-console.html", + category: CATEGORY_NETWORK, + }, + { + text: "foobarz2", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(hud.outputNode.textContent.indexOf("foobarz1"), -1, + "foobarz1 has been removed from output"); + + function* openSidebar(objName, expectedObj) { + let msg = yield hud.jsterm.execute(objName); + ok(msg, "output message found"); + + let anchor = msg.querySelector("a"); + let body = msg.querySelector(".message-body"); + ok(anchor, "object anchor"); + ok(body, "message body"); + + yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow); + + let vviewVar = yield hud.jsterm.once("variablesview-fetched"); + let vview = vviewVar._variablesView; + ok(vview, "variables view object exists"); + + yield findVariableViewProperties(vviewVar, [ + expectedObj, + ], { webconsole: hud }); + } +}); diff --git a/devtools/client/webconsole/test/browser_console_click_focus.js b/devtools/client/webconsole/test/browser_console_click_focus.js new file mode 100644 index 000000000..f405f0bbf --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_click_focus.js @@ -0,0 +1,59 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the input field is focused when the console is opened. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Dolske Digs Bacon", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let msg = [...result.matched][0]; + let outputItem = msg.querySelector(".message-body"); + ok(outputItem, "found a logged message"); + + let inputNode = hud.jsterm.inputNode; + ok(inputNode.getAttribute("focused"), "input node is focused, first"); + + yield waitForBlurredInput(inputNode); + + EventUtils.sendMouseEvent({type: "click"}, hud.outputNode); + ok(inputNode.getAttribute("focused"), "input node is focused, second time"); + + yield waitForBlurredInput(inputNode); + + info("Setting a text selection and making sure a click does not re-focus"); + let selection = hud.iframeWindow.getSelection(); + selection.selectAllChildren(outputItem); + + EventUtils.sendMouseEvent({type: "click"}, hud.outputNode); + ok(!inputNode.getAttribute("focused"), + "input node is not focused after drag"); +}); + +function waitForBlurredInput(inputNode) { + return new Promise(resolve => { + let lostFocus = () => { + inputNode.removeEventListener("blur", lostFocus); + ok(!inputNode.getAttribute("focused"), "input node is not focused"); + resolve(); + }; + inputNode.addEventListener("blur", lostFocus); + document.getElementById("urlbar").click(); + }); +} diff --git a/devtools/client/webconsole/test/browser_console_consolejsm_output.js b/devtools/client/webconsole/test/browser_console_consolejsm_output.js new file mode 100644 index 000000000..e5b37843e --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_consolejsm_output.js @@ -0,0 +1,285 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that Console.jsm outputs messages to the Browser Console, bug 851231. + +"use strict"; + +function onNewMessage(aEvent, aNewMessages) { + for (let msg of aNewMessages) { + // Messages that shouldn't be output contain the substring FAIL_TEST + if (msg.node.textContent.includes("FAIL_TEST")) { + ok(false, "Message shouldn't have been output: " + msg.node.textContent); + } + } +} + +add_task(function* () { + let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"]; + let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage); + storage.clearEvents(); + + let {console} = Cu.import("resource://gre/modules/Console.jsm", {}); + console.log("bug861338-log-cached"); + + let hud = yield HUDService.toggleBrowserConsole(); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "cached console.log message", + text: "bug861338-log-cached", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + hud.jsterm.clearOutput(true); + + function testTrace() { + console.trace(); + } + + console.time("foobarTimer"); + let foobar = { bug851231prop: "bug851231value" }; + + console.log("bug851231-log"); + console.info("bug851231-info"); + console.warn("bug851231-warn"); + console.error("bug851231-error", foobar); + console.debug("bug851231-debug"); + console.dir(document); + testTrace(); + console.timeEnd("foobarTimer"); + + info("wait for the Console.jsm messages"); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "console.log output", + text: "bug851231-log", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "console.info output", + text: "bug851231-info", + category: CATEGORY_WEBDEV, + severity: SEVERITY_INFO, + }, + { + name: "console.warn output", + text: "bug851231-warn", + category: CATEGORY_WEBDEV, + severity: SEVERITY_WARNING, + }, + { + name: "console.error output", + text: /\bbug851231-error\b.+\{\s*bug851231prop:\s"bug851231value"\s*\}/, + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + objects: true, + }, + { + name: "console.debug output", + text: "bug851231-debug", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "console.trace output", + consoleTrace: { + file: "browser_console_consolejsm_output.js", + fn: "testTrace", + }, + }, + { + name: "console.dir output", + consoleDir: /XULDocument\s+.+\s+chrome:\/\/.+\/browser\.xul/, + }, + { + name: "console.time output", + consoleTime: "foobarTimer", + }, + { + name: "console.timeEnd output", + consoleTimeEnd: "foobarTimer", + }, + ], + }); + + let consoleErrorMsg = results[3]; + ok(consoleErrorMsg, "console.error message element found"); + let clickable = consoleErrorMsg.clickableElements[0]; + ok(clickable, "clickable object found for console.error"); + + let deferred = promise.defer(); + + let onFetch = (aEvent, aVar) => { + // Skip the notification from console.dir variablesview-fetched. + if (aVar._variablesView != hud.jsterm._variablesView) { + return; + } + hud.jsterm.off("variablesview-fetched", onFetch); + + deferred.resolve(aVar); + }; + + hud.jsterm.on("variablesview-fetched", onFetch); + + clickable.scrollIntoView(false); + + info("wait for variablesview-fetched"); + executeSoon(() => + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow)); + + let varView = yield deferred.promise; + ok(varView, "object inspector opened on click"); + + yield findVariableViewProperties(varView, [{ + name: "bug851231prop", + value: "bug851231value", + }], { webconsole: hud }); + + yield HUDService.toggleBrowserConsole(); +}); + +add_task(function* testPrefix() { + let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"]; + let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage); + storage.clearEvents(); + + let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); + let consoleOptions = { + maxLogLevel: "error", + prefix: "Log Prefix", + }; + let console2 = new ConsoleAPI(consoleOptions); + console2.error("Testing a prefix"); + console2.log("FAIL_TEST: Below the maxLogLevel"); + + let hud = yield HUDService.toggleBrowserConsole(); + hud.ui.on("new-messages", onNewMessage); + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "cached console.error message", + prefix: "Log Prefix:", + severity: SEVERITY_ERROR, + text: "Testing a prefix", + }], + }); + + hud.jsterm.clearOutput(true); + hud.ui.off("new-messages", onNewMessage); + yield HUDService.toggleBrowserConsole(); +}); + +add_task(function* testMaxLogLevelPrefMissing() { + let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"]; + let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage); + storage.clearEvents(); + + let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); + let consoleOptions = { + maxLogLevel: "error", + maxLogLevelPref: "testing.maxLogLevel", + }; + let console = new ConsoleAPI(consoleOptions); + + is(Services.prefs.getPrefType(consoleOptions.maxLogLevelPref), + Services.prefs.PREF_INVALID, + "Check log level pref is missing"); + + // Since the maxLogLevelPref doesn't exist, we should fallback to the passed + // maxLogLevel of "error". + console.warn("FAIL_TEST: Below the maxLogLevel"); + console.error("Error should be shown"); + + let hud = yield HUDService.toggleBrowserConsole(); + + hud.ui.on("new-messages", onNewMessage); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "defaulting to error level", + severity: SEVERITY_ERROR, + text: "Error should be shown", + }], + }); + + hud.jsterm.clearOutput(true); + hud.ui.off("new-messages", onNewMessage); + yield HUDService.toggleBrowserConsole(); +}); + +add_task(function* testMaxLogLevelPref() { + let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"]; + let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage); + storage.clearEvents(); + + let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {}); + let consoleOptions = { + maxLogLevel: "error", + maxLogLevelPref: "testing.maxLogLevel", + }; + + info("Setting the pref to warn"); + Services.prefs.setCharPref(consoleOptions.maxLogLevelPref, "Warn"); + + let console = new ConsoleAPI(consoleOptions); + + is(console.maxLogLevel, "warn", "Check pref was read at initialization"); + + console.info("FAIL_TEST: info is below the maxLogLevel"); + console.error("Error should be shown"); + console.warn("Warn should be shown due to the initial pref value"); + + info("Setting the pref to info"); + Services.prefs.setCharPref(consoleOptions.maxLogLevelPref, "INFO"); + is(console.maxLogLevel, "info", "Check pref was lowercased"); + + console.info("info should be shown due to the pref change being observed"); + + info("Clearing the pref"); + Services.prefs.clearUserPref(consoleOptions.maxLogLevelPref); + + console.warn("FAIL_TEST: Shouldn't be shown due to defaulting to error"); + console.error("Should be shown due to defaulting to error"); + + let hud = yield HUDService.toggleBrowserConsole(); + hud.ui.on("new-messages", onNewMessage); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "error > warn", + severity: SEVERITY_ERROR, + text: "Error should be shown", + }, + { + name: "warn is the inital pref value", + severity: SEVERITY_WARNING, + text: "Warn should be shown due to the initial pref value", + }, + { + name: "pref changed to info", + severity: SEVERITY_INFO, + text: "info should be shown due to the pref change being observed", + }, + { + name: "default to intial maxLogLevel if pref is removed", + severity: SEVERITY_ERROR, + text: "Should be shown due to defaulting to error", + }], + }); + + hud.jsterm.clearOutput(true); + hud.ui.off("new-messages", onNewMessage); + yield HUDService.toggleBrowserConsole(); +}); diff --git a/devtools/client/webconsole/test/browser_console_copy_command.js b/devtools/client/webconsole/test/browser_console_copy_command.js new file mode 100644 index 000000000..c4ed4360f --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_copy_command.js @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the `copy` console helper works as intended. + +"use strict"; + +var gWebConsole, gJSTerm; + +var TEXT = "Lorem ipsum dolor sit amet, consectetur adipisicing " + + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " + + "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " + + "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + + "dolor in reprehenderit in voluptate velit esse cillum dolore eu " + + "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum." + + new Date(); + +var ID = "select-me"; + +add_task(function* init() { + yield loadTab("data:text/html;charset=utf-8," + + "<body>" + + " <div>" + + " <h1>Testing copy command</h1>" + + " <p>This is some example text</p>" + + " <p id='select-me'>" + TEXT + "</p>" + + " </div>" + + " <div><p></p></div>" + + "</body>"); + + gWebConsole = yield openConsole(); + gJSTerm = gWebConsole.jsterm; +}); + +add_task(function* testCopy() { + let RANDOM = Math.random(); + let string = "Text: " + RANDOM; + let obj = {a: 1, b: "foo", c: RANDOM}; + + let samples = [ + [RANDOM, RANDOM], + [JSON.stringify(string), string], + [obj.toSource(), JSON.stringify(obj, null, " ")], + [ + "$('#" + ID + "')", + content.document.getElementById(ID).outerHTML + ] + ]; + for (let [source, reference] of samples) { + let deferredResult = promise.defer(); + + SimpleTest.waitForClipboard( + "" + reference, + () => { + let command = "copy(" + source + ")"; + info("Attempting to copy: " + source); + info("Executing command: " + command); + gJSTerm.execute(command, msg => { + is(msg, undefined, "Command success: " + command); + }); + }, + deferredResult.resolve, + deferredResult.reject); + + yield deferredResult.promise; + } +}); + +add_task(function* cleanup() { + gWebConsole = gJSTerm = null; + gBrowser.removeTab(gBrowser.selectedTab); + finishTest(); +}); diff --git a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js new file mode 100644 index 000000000..bdd4f7179 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* globals goDoCommand */ + +"use strict"; + +// Test copying of the entire console message when right-clicked +// with no other text selected. See Bug 1100562. + +add_task(function* () { + let hud; + let outputNode; + let contextMenu; + + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html"; + + const { tab, browser } = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + outputNode = hud.outputNode; + contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu"); + + registerCleanupFunction(() => { + hud = outputNode = contextMenu = null; + }); + + hud.jsterm.clearOutput(); + + yield ContentTask.spawn(browser, {}, function* () { + let button = content.document.getElementById("testTrace"); + button.click(); + }); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "bug 1100562", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + lines: 1, + }, + { + name: "console.trace output", + consoleTrace: true, + lines: 3, + }, + ] + }); + + outputNode.focus(); + + for (let result of results) { + let message = [...result.matched][0]; + + yield waitForContextMenu(contextMenu, message, () => { + let copyItem = contextMenu.querySelector("#cMenu_copy"); + copyItem.doCommand(); + + let controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); + }); + + let clipboardText; + + yield waitForClipboardPromise( + () => goDoCommand("cmd_copy"), + (str) => { + clipboardText = str; + return message.textContent == clipboardText; + } + ); + + ok(clipboardText, "Clipboard text was found and saved"); + + let lines = clipboardText.split("\n"); + ok(lines.length > 0, "There is at least one newline in the message"); + is(lines.pop(), "", "There is a newline at the end"); + is(lines.length, result.lines, `There are ${result.lines} lines in the message`); + + // Test the first line for "timestamp message repeat file:line" + let firstLine = lines.shift(); + ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine), + "The message's first line has the right format"); + + // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col" + for (let line of lines) { + ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format"); + } + } + + yield closeConsole(tab); + yield finishTest(); +}); diff --git a/devtools/client/webconsole/test/browser_console_dead_objects.js b/devtools/client/webconsole/test/browser_console_dead_objects.js new file mode 100644 index 000000000..46b15d59b --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_dead_objects.js @@ -0,0 +1,88 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that Dead Objects do not break the Web/Browser Consoles. +// See bug 883649. +// This test does: +// - opens a new tab, +// - opens the Browser Console, +// - stores a reference to the content document of the tab on the chrome +// window object, +// - closes the tab, +// - tries to use the object that was pointing to the now-defunct content +// document. This is the dead object. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>dead objects!"; + +function test() { + let hud = null; + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("devtools.chrome.enabled"); + }); + + Task.spawn(runner).then(finishTest); + + function* runner() { + Services.prefs.setBoolPref("devtools.chrome.enabled", true); + yield loadTab(TEST_URI); + + info("open the browser console"); + + hud = yield HUDService.toggleBrowserConsole(); + ok(hud, "browser console opened"); + + let jsterm = hud.jsterm; + + jsterm.clearOutput(); + + // Add the reference to the content document. + yield jsterm.execute("Cu = Components.utils;" + + "Cu.import('resource://gre/modules/Services.jsm');" + + "chromeWindow = Services.wm.getMostRecentWindow('" + + "navigator:browser');" + + "foobarzTezt = chromeWindow.content.document;" + + "delete chromeWindow"); + + gBrowser.removeCurrentTab(); + + let msg = yield jsterm.execute("foobarzTezt"); + + isnot(hud.outputNode.textContent.indexOf("[object DeadObject]"), -1, + "dead object found"); + + jsterm.setInputValue("foobarzTezt"); + + for (let c of ".hello") { + EventUtils.synthesizeKey(c, {}, hud.iframeWindow); + } + + yield jsterm.execute(); + + isnot(hud.outputNode.textContent.indexOf("can't access dead object"), -1, + "'cannot access dead object' message found"); + + // Click the second execute output. + let clickable = msg.querySelector("a"); + ok(clickable, "clickable object found"); + isnot(clickable.textContent.indexOf("[object DeadObject]"), -1, + "message text check"); + + msg.scrollIntoView(); + + executeSoon(() => { + EventUtils.synthesizeMouseAtCenter(clickable, {}, hud.iframeWindow); + }); + + yield jsterm.once("variablesview-fetched"); + ok(true, "variables view fetched"); + + msg = yield jsterm.execute("delete window.foobarzTezt; 2013-26"); + + isnot(msg.textContent.indexOf("1987"), -1, "result message found"); + } +} diff --git a/devtools/client/webconsole/test/browser_console_error_source_click.js b/devtools/client/webconsole/test/browser_console_error_source_click.js new file mode 100644 index 000000000..5839f20d5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_error_source_click.js @@ -0,0 +1,79 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that JS errors and CSS warnings open view source when their source link +// is clicked in the Browser Console. See bug 877778. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>hello world from bug 877778 " + + "<button onclick='foobar.explode()' " + + "style='test-color: green-please'>click!</button>"; + +add_task(function* () { + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["devtools.browserconsole.filter.cssparser", true] + ]}, resolve); + }); + + yield loadTab(TEST_URI); + let hud = yield HUDService.toggleBrowserConsole(); + ok(hud, "browser console opened"); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + info("generate exception and wait for the message"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let button = content.document.querySelector("button"); + button.click(); + }); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "ReferenceError: foobar is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "Unknown property \u2018test-color\u2019", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }, + ], + }); + + let viewSourceCalled = false; + + let viewSource = hud.viewSource; + hud.viewSource = () => { + viewSourceCalled = true; + }; + + for (let result of results) { + viewSourceCalled = false; + + let msg = [...result.matched][0]; + ok(msg, "message element found for: " + result.text); + ok(!msg.classList.contains("filtered-by-type"), "message element is not filtered"); + let selector = ".message .message-location .frame-link-source"; + let locationNode = msg.querySelector(selector); + ok(locationNode, "message location element found"); + + locationNode.click(); + + ok(viewSourceCalled, "view source opened"); + } + + hud.viewSource = viewSource; + + yield finishTest(); +}); diff --git a/devtools/client/webconsole/test/browser_console_filters.js b/devtools/client/webconsole/test/browser_console_filters.js new file mode 100644 index 000000000..072766fdb --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_filters.js @@ -0,0 +1,60 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the Browser Console does not use the same filter prefs as the Web +// Console. See bug 878186. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>browser console filters"; +const WEB_CONSOLE_PREFIX = "devtools.webconsole.filter."; +const BROWSER_CONSOLE_PREFIX = "devtools.browserconsole.filter."; + +add_task(function* () { + yield loadTab(TEST_URI); + + info("open the web console"); + let hud = yield openConsole(); + ok(hud, "web console opened"); + + is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (browser console)"); + is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (web console)"); + + info("toggle 'exception' filter"); + hud.setFilterState("exception", false); + + is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (browser console)"); + is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), false, + "'exception' filter is disabled (web console)"); + + hud.setFilterState("exception", true); + + // We need to let the console opening event loop to finish. + let deferred = promise.defer(); + executeSoon(() => closeConsole().then(() => deferred.resolve(null))); + yield deferred.promise; + + info("web console closed"); + hud = yield HUDService.toggleBrowserConsole(); + ok(hud, "browser console opened"); + + is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (browser console)"); + is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (web console)"); + + info("toggle 'exception' filter"); + hud.setFilterState("exception", false); + + is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), false, + "'exception' filter is disabled (browser console)"); + is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true, + "'exception' filter is enabled (web console)"); + + hud.setFilterState("exception", true); +}); diff --git a/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js b/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js new file mode 100644 index 000000000..d3fdb08be --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* + * Bug 922161 - Hide Browser Console JS input field if devtools.chrome.enabled + * is false. + * when devtools.chrome.enabled then + * -browser console jsterm should be enabled + * -browser console object inspector properties should be set. + * -webconsole jsterm should be enabled + * -webconsole object inspector properties should be set. + * + * when devtools.chrome.enabled == false then + * -browser console jsterm should be disabled + * -browser console object inspector properties should not be set. + * -webconsole jsterm should be enabled + * -webconsole object inspector properties should be set. + */ + +"use strict"; + +function testObjectInspectorPropertiesAreNotSet(variablesView) { + is(variablesView.eval, null, "vview.eval is null"); + is(variablesView.switch, null, "vview.switch is null"); + is(variablesView.delete, null, "vview.delete is null"); +} + +function* getVariablesView(hud) { + function openVariablesView(event, vview) { + deferred.resolve(vview._variablesView); + } + + let deferred = promise.defer(); + + // Filter out other messages to ensure ours stays visible. + hud.ui.filterBox.value = "browser_console_hide_jsterm_test"; + + hud.jsterm.clearOutput(); + hud.jsterm.execute("new Object({ browser_console_hide_jsterm_test: true })"); + + let [message] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Object { browser_console_hide_jsterm_test: true }", + category: CATEGORY_OUTPUT, + }], + }); + + hud.jsterm.once("variablesview-fetched", openVariablesView); + + let anchor = [...message.matched][0].querySelector("a"); + + executeSoon(() => + EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow) + ); + + return deferred.promise; +} + +function testJSTermIsVisible(hud) { + let inputContainer = hud.ui.window.document + .querySelector(".jsterm-input-container"); + isnot(inputContainer.style.display, "none", "input is visible"); +} + +function testObjectInspectorPropertiesAreSet(variablesView) { + isnot(variablesView.eval, null, "vview.eval is set"); + isnot(variablesView.switch, null, "vview.switch is set"); + isnot(variablesView.delete, null, "vview.delete is set"); +} + +function testJSTermIsNotVisible(hud) { + let inputContainer = hud.ui.window.document + .querySelector(".jsterm-input-container"); + is(inputContainer.style.display, "none", "input is not visible"); +} + +function* testRunner() { + let browserConsole, webConsole, variablesView; + + Services.prefs.setBoolPref("devtools.chrome.enabled", true); + + browserConsole = yield HUDService.toggleBrowserConsole(); + variablesView = yield getVariablesView(browserConsole); + testJSTermIsVisible(browserConsole); + testObjectInspectorPropertiesAreSet(variablesView); + + let {tab: browserTab} = yield loadTab("data:text/html;charset=utf8,hello world"); + webConsole = yield openConsole(browserTab); + variablesView = yield getVariablesView(webConsole); + testJSTermIsVisible(webConsole); + testObjectInspectorPropertiesAreSet(variablesView); + yield closeConsole(browserTab); + + yield HUDService.toggleBrowserConsole(); + Services.prefs.setBoolPref("devtools.chrome.enabled", false); + + browserConsole = yield HUDService.toggleBrowserConsole(); + variablesView = yield getVariablesView(browserConsole); + testJSTermIsNotVisible(browserConsole); + testObjectInspectorPropertiesAreNotSet(variablesView); + + webConsole = yield openConsole(browserTab); + variablesView = yield getVariablesView(webConsole); + testJSTermIsVisible(webConsole); + testObjectInspectorPropertiesAreSet(variablesView); + yield closeConsole(browserTab); +} + +function test() { + Task.spawn(testRunner).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_console_history_persist.js b/devtools/client/webconsole/test/browser_console_history_persist.js new file mode 100644 index 000000000..61c4cbf4d --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_history_persist.js @@ -0,0 +1,119 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that console command input is persisted across toolbox loads. +// See Bug 943306. + +"use strict"; + +requestLongerTimeout(2); + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "persisting history - bug 943306"; +const INPUT_HISTORY_COUNT = 10; + +add_task(function* () { + info("Setting custom input history pref to " + INPUT_HISTORY_COUNT); + Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount", + INPUT_HISTORY_COUNT); + + // First tab: run a bunch of commands and then make sure that you can + // navigate through their history. + yield loadTab(TEST_URI); + let hud1 = yield openConsole(); + is(JSON.stringify(hud1.jsterm.history), "[]", + "No history on first tab initially"); + yield populateInputHistory(hud1); + is(JSON.stringify(hud1.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9"]', + "First tab has populated history"); + + // Second tab: Just make sure that you can navigate through the history + // generated by the first tab. + yield loadTab(TEST_URI); + let hud2 = yield openConsole(); + is(JSON.stringify(hud2.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9"]', + "Second tab has populated history"); + yield testNaviatingHistoryInUI(hud2); + is(JSON.stringify(hud2.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9",""]', + "An empty entry has been added in the second tab due to history perusal"); + + // Third tab: Should have the same history as first tab, but if we run a + // command, then the history of the first and second shouldn't be affected + yield loadTab(TEST_URI); + let hud3 = yield openConsole(); + is(JSON.stringify(hud3.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9"]', + "Third tab has populated history"); + + // Set input value separately from execute so UP arrow accurately navigates + // history. + hud3.jsterm.setInputValue('"hello from third tab"'); + hud3.jsterm.execute(); + + is(JSON.stringify(hud1.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9"]', + "First tab history hasn't changed due to command in third tab"); + is(JSON.stringify(hud2.jsterm.history), + '["0","1","2","3","4","5","6","7","8","9",""]', + "Second tab history hasn't changed due to command in third tab"); + is(JSON.stringify(hud3.jsterm.history), + '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]', + "Third tab has updated history (and purged the first result) after " + + "running a command"); + + // Fourth tab: Should have the latest command from the third tab, followed + // by the rest of the history from the first tab. + yield loadTab(TEST_URI); + let hud4 = yield openConsole(); + is(JSON.stringify(hud4.jsterm.history), + '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]', + "Fourth tab has most recent history"); + + yield hud4.jsterm.clearHistory(); + is(JSON.stringify(hud4.jsterm.history), "[]", + "Clearing history for a tab works"); + + yield loadTab(TEST_URI); + let hud5 = yield openConsole(); + is(JSON.stringify(hud5.jsterm.history), "[]", + "Clearing history carries over to a new tab"); + + info("Clearing custom input history pref"); + Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount"); +}); + +/** + * Populate the history by running the following commands: + * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + */ +function* populateInputHistory(hud) { + let jsterm = hud.jsterm; + + for (let i = 0; i < INPUT_HISTORY_COUNT; i++) { + // Set input value separately from execute so UP arrow accurately navigates + // history. + jsterm.setInputValue(i); + jsterm.execute(); + } +} + +/** + * Check pressing up results in history traversal like: + * [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + */ +function* testNaviatingHistoryInUI(hud) { + let jsterm = hud.jsterm; + jsterm.focus(); + + // Count backwards from original input and make sure that pressing up + // restores this. + for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) { + EventUtils.synthesizeKey("VK_UP", {}); + is(jsterm.getInputValue(), i, "Pressing up restores last input"); + } +} diff --git a/devtools/client/webconsole/test/browser_console_iframe_messages.js b/devtools/client/webconsole/test/browser_console_iframe_messages.js new file mode 100644 index 000000000..9bf3fe2b7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_iframe_messages.js @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that cached messages from nested iframes are displayed in the +// Web/Browser Console. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-consoleiframes.html"; + +const expectedMessages = [ + { + text: "main file", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "blah", + category: CATEGORY_JS, + severity: SEVERITY_ERROR + }, + { + text: "iframe 2", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG + }, + { + text: "iframe 3", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG + } +]; + +// "iframe 1" console messages can be coalesced into one if they follow each +// other in the sequence of messages (depending on timing). If they do not, then +// they will be displayed in the console output independently, as separate +// messages. This is why we need to match any of the following two rules. +const expectedMessagesAny = [ + { + name: "iframe 1 (count: 2)", + text: "iframe 1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 2 + }, + { + name: "iframe 1 (repeats: 2)", + text: "iframe 1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 2 + }, +]; + +add_task(function* () { + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + yield loadTab(TEST_URI); + let hud = yield openConsole(); + ok(hud, "web console opened"); + + yield testWebConsole(hud); + yield closeConsole(); + info("web console closed"); + + hud = yield HUDService.toggleBrowserConsole(); + yield testBrowserConsole(hud); + yield closeConsole(); +}); + +function* testWebConsole(hud) { + yield waitForMessages({ + webconsole: hud, + messages: expectedMessages, + }); + + info("first messages matched"); + + yield waitForMessages({ + webconsole: hud, + messages: expectedMessagesAny, + matchCondition: "any", + }); +} + +function* testBrowserConsole(hud) { + ok(hud, "browser console opened"); + + // TODO: The browser console doesn't show page's console.log statements + // in e10s windows. See Bug 1241289. + if (Services.appinfo.browserTabsRemoteAutostart) { + todo(false, "Bug 1241289"); + return; + } + + yield waitForMessages({ + webconsole: hud, + messages: expectedMessages, + }); + + info("first messages matched"); + yield waitForMessages({ + webconsole: hud, + messages: expectedMessagesAny, + matchCondition: "any", + }); +} diff --git a/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js new file mode 100644 index 000000000..c64e45f5d --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js @@ -0,0 +1,89 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that basic keyboard shortcuts work in the web console. + +"use strict"; + +add_task(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + ok(hud, "Web Console opened"); + + info("dump some spew into the console for scrolling"); + hud.jsterm.execute("(function() { for (var i = 0; i < 100; i++) { " + + "console.log('foobarz' + i);" + + "}})();"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarz99", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let currentPosition = hud.ui.outputWrapper.scrollTop; + let bottom = currentPosition; + + EventUtils.synthesizeKey("VK_PAGE_UP", {}); + isnot(hud.ui.outputWrapper.scrollTop, currentPosition, + "scroll position changed after page up"); + + currentPosition = hud.ui.outputWrapper.scrollTop; + EventUtils.synthesizeKey("VK_PAGE_DOWN", {}); + ok(hud.ui.outputWrapper.scrollTop > currentPosition, + "scroll position now at bottom"); + + EventUtils.synthesizeKey("VK_HOME", {}); + is(hud.ui.outputWrapper.scrollTop, 0, "scroll position now at top"); + + EventUtils.synthesizeKey("VK_END", {}); + + let scrollTop = hud.ui.outputWrapper.scrollTop; + ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5, + "scroll position now at bottom"); + + info("try ctrl-l to clear output"); + executeSoon(() => { + let clearShortcut; + if (Services.appinfo.OS === "Darwin") { + clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX"); + } else { + clearShortcut = WCUL10n.getStr("webconsole.clear.key"); + } + synthesizeKeyShortcut(clearShortcut); + }); + yield hud.jsterm.once("messages-cleared"); + + is(hud.outputNode.textContent.indexOf("foobarz1"), -1, "output cleared"); + is(hud.jsterm.inputNode.getAttribute("focused"), "true", + "jsterm input is focused"); + + info("try ctrl-f to focus filter"); + synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key")); + ok(!hud.jsterm.inputNode.getAttribute("focused"), + "jsterm input is not focused"); + is(hud.ui.filterBox.getAttribute("focused"), "true", + "filter input is focused"); + + if (Services.appinfo.OS == "Darwin") { + ok(hud.ui.getFilterState("network"), "network category is enabled"); + EventUtils.synthesizeKey("t", { ctrlKey: true }); + ok(!hud.ui.getFilterState("network"), "accesskey for Network works"); + EventUtils.synthesizeKey("t", { ctrlKey: true }); + ok(hud.ui.getFilterState("network"), "accesskey for Network works (again)"); + } else { + EventUtils.synthesizeKey("N", { altKey: true }); + let net = hud.ui.document.querySelector("toolbarbutton[category=net]"); + is(hud.ui.document.activeElement, net, + "accesskey for Network category focuses the Net button"); + } +}); diff --git a/devtools/client/webconsole/test/browser_console_log_inspectable_object.js b/devtools/client/webconsole/test/browser_console_log_inspectable_object.js new file mode 100644 index 000000000..f9fd85295 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_log_inspectable_object.js @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that objects given to console.log() are inspectable. + +"use strict"; + +add_task(function* () { + yield loadTab("data:text/html;charset=utf8,test for bug 676722 - " + + "inspectable objects for window.console"); + + let hud = yield openConsole(); + hud.jsterm.clearOutput(true); + + yield hud.jsterm.execute("myObj = {abba: 'omgBug676722'}"); + hud.jsterm.execute("console.log('fooBug676722', myObj)"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "fooBug676722", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + objects: true, + }], + }); + + let msg = [...result.matched][0]; + ok(msg, "message element"); + + let body = msg.querySelector(".message-body"); + ok(body, "message body"); + + let clickable = result.clickableElements[0]; + ok(clickable, "the console.log() object anchor was found"); + ok(body.textContent.includes('{ abba: "omgBug676722" }'), + "clickable node content is correct"); + + executeSoon(() => { + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + }); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + ok(varView, "object inspector opened on click"); + + yield findVariableViewProperties(varView, [{ + name: "abba", + value: "omgBug676722", + }], { webconsole: hud }); +}); diff --git a/devtools/client/webconsole/test/browser_console_native_getters.js b/devtools/client/webconsole/test/browser_console_native_getters.js new file mode 100644 index 000000000..1afb70796 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_native_getters.js @@ -0,0 +1,101 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that native getters and setters for DOM elements work as expected in +// variables view - bug 870220. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<title>bug870220</title>\n" + + "<p>hello world\n<p>native getters!"; + +requestLongerTimeout(2); + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + let jsterm = hud.jsterm; + + jsterm.execute("document"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "HTMLDocument \u2192 data:text/html;charset=utf8", + category: CATEGORY_OUTPUT, + objects: true, + }], + }); + + let clickable = result.clickableElements[0]; + ok(clickable, "clickable object found"); + + executeSoon(() => { + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + }); + + let fetchedVar = yield jsterm.once("variablesview-fetched"); + + let variablesView = fetchedVar._variablesView; + ok(variablesView, "variables view object"); + + let results = yield findVariableViewProperties(fetchedVar, [ + { name: "title", value: "bug870220" }, + { name: "bgColor" }, + ], { webconsole: hud }); + + let prop = results[1].matchedProp; + ok(prop, "matched the |bgColor| property in the variables view"); + + // Check that property value updates work. + let updatedVar = yield updateVariablesViewProperty({ + property: prop, + field: "value", + string: "'red'", + webconsole: hud, + }); + + info("on fetch after background update"); + + jsterm.clearOutput(true); + jsterm.execute("document.bgColor"); + + [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "red", + category: CATEGORY_OUTPUT, + }], + }); + + yield findVariableViewProperties(updatedVar, [ + { name: "bgColor", value: "red" }, + ], { webconsole: hud }); + + jsterm.execute("$$('p')"); + + [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Array [", + category: CATEGORY_OUTPUT, + objects: true, + }], + }); + + clickable = result.clickableElements[0]; + ok(clickable, "clickable object found"); + + executeSoon(() => { + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + }); + + fetchedVar = yield jsterm.once("variablesview-fetched"); + + yield findVariableViewProperties(fetchedVar, [ + { name: "0.textContent", value: /hello world/ }, + { name: "1.textContent", value: /native getters/ }, + ], { webconsole: hud }); +}); diff --git a/devtools/client/webconsole/test/browser_console_navigation_marker.js b/devtools/client/webconsole/test/browser_console_navigation_marker.js new file mode 100644 index 000000000..e8ec84caf --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_navigation_marker.js @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the navigation marker shows on page reload - bug 793996. + +"use strict"; + +const PREF = "devtools.webconsole.persistlog"; +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +var hud; + +add_task(function* () { + Services.prefs.setBoolPref(PREF, true); + + let { browser } = yield loadTab(TEST_URI); + hud = yield openConsole(); + + yield consoleOpened(); + + let loaded = loadBrowser(browser); + BrowserReload(); + yield loaded; + + yield onReload(); + + isnot(hud.outputNode.textContent.indexOf("foobarz1"), -1, + "foobarz1 is still in the output"); + + Services.prefs.clearUserPref(PREF); + + hud = null; +}); + +function consoleOpened() { + ok(hud, "Web Console opened"); + + hud.jsterm.clearOutput(); + + ContentTask.spawn(gBrowser.selectedBrowser, null, function* () { + content.console.log("foobarz1"); + }); + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarz1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} + +function onReload() { + ContentTask.spawn(gBrowser.selectedBrowser, null, function* () { + content.console.log("foobarz2"); + }); + + return waitForMessages({ + webconsole: hud, + messages: [{ + name: "page reload", + text: "test-console.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "foobarz2", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "navigation marker", + text: "test-console.html", + type: Messages.NavigationMarker, + }], + }); +} diff --git a/devtools/client/webconsole/test/browser_console_netlogging.js b/devtools/client/webconsole/test/browser_console_netlogging.js new file mode 100644 index 000000000..a6f7bec48 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_netlogging.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that network log messages bring up the network panel. + +"use strict"; + +const TEST_NETWORK_REQUEST_URI = + "http://example.com/browser/devtools/client/webconsole/test/" + + "test-network-request.html"; + +add_task(function* () { + let finishedRequest = waitForFinishedRequest(({ request }) => { + return request.url === TEST_NETWORK_REQUEST_URI; + }); + + const hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI, + "browserConsole"); + let request = yield finishedRequest; + + ok(request, "Page load was logged"); + + let client = hud.ui.webConsoleClient; + let args = [request.actor]; + const postData = yield getPacket(client, "getRequestPostData", args); + const responseContent = yield getPacket(client, "getResponseContent", args); + + is(request.request.url, TEST_NETWORK_REQUEST_URI, + "Logged network entry is page load"); + is(request.request.method, "GET", "Method is correct"); + ok(!postData.postData.text, "No request body was stored"); + ok(postData.postDataDiscarded, "Request body was discarded"); + ok(!responseContent.content.text, "No response body was stored"); + ok(responseContent.contentDiscarded || request.fromCache, + "Response body was discarded or response came from the cache"); +}); diff --git a/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js b/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js new file mode 100644 index 000000000..fedd0c71c --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that nsIConsoleMessages are displayed in the Browser Console. +// See bug 859756. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<title>bug859756</title>\n" + + "<p>hello world\n<p>nsIConsoleMessages ftw!"; + +function test() { + const FILTER_PREF = "devtools.browserconsole.filter.jslog"; + Services.prefs.setBoolPref(FILTER_PREF, true); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref(FILTER_PREF); + }); + + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + + // Test for cached nsIConsoleMessages. + Services.console.logStringMessage("test1 for bug859756"); + + info("open web console"); + let hud = yield openConsole(tab); + + ok(hud, "web console opened"); + Services.console.logStringMessage("do-not-show-me"); + + ContentTask.spawn(gBrowser.selectedBrowser, null, function* () { + content.console.log("foobarz"); + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarz", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let text = hud.outputNode.textContent; + is(text.indexOf("do-not-show-me"), -1, + "nsIConsoleMessages are not displayed"); + is(text.indexOf("test1 for bug859756"), -1, + "nsIConsoleMessages are not displayed (confirmed)"); + + yield closeConsole(tab); + + info("web console closed"); + hud = yield HUDService.toggleBrowserConsole(); + ok(hud, "browser console opened"); + + Services.console.logStringMessage("test2 for bug859756"); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test1 for bug859756", + category: CATEGORY_JS, + }, { + text: "test2 for bug859756", + category: CATEGORY_JS, + }, { + text: "do-not-show-me", + category: CATEGORY_JS, + }], + }); + + let msg = [...results[2].matched][0]; + ok(msg, "message element for do-not-show-me (nsIConsoleMessage)"); + isnot(msg.textContent.indexOf("do-not-show"), -1, + "element content is correct"); + ok(!msg.classList.contains("filtered-by-type"), "element is not filtered"); + + hud.setFilterState("jslog", false); + + ok(msg.classList.contains("filtered-by-type"), "element is filtered"); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_console_open_or_focus.js b/devtools/client/webconsole/test/browser_console_open_or_focus.js new file mode 100644 index 000000000..d537c9aad --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_open_or_focus.js @@ -0,0 +1,46 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the "browser console" menu item opens or focuses (if already open) +// the console window instead of toggling it open/close. + +"use strict"; + +var {Tools} = require("devtools/client/definitions"); + +add_task(function* () { + let currWindow, hud, mainWindow; + + mainWindow = Services.wm.getMostRecentWindow(null); + + yield HUDService.openBrowserConsoleOrFocus(); + + hud = HUDService.getBrowserConsole(); + + console.log("testmessage"); + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "testmessage" + }], + }); + + currWindow = Services.wm.getMostRecentWindow(null); + is(currWindow.document.documentURI, Tools.webConsole.url, + "The Browser Console is open and has focus"); + + mainWindow.focus(); + + yield HUDService.openBrowserConsoleOrFocus(); + + currWindow = Services.wm.getMostRecentWindow(null); + is(currWindow.document.documentURI, Tools.webConsole.url, + "The Browser Console is open and has focus"); + + yield HUDService.toggleBrowserConsole(); + + hud = HUDService.getBrowserConsole(); + ok(!hud, "Browser Console has been closed"); +}); diff --git a/devtools/client/webconsole/test/browser_console_optimized_out_vars.js b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js new file mode 100644 index 000000000..dc898eb2b --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that inspecting an optimized out variable works when execution is +// paused. + +"use strict"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function test() { + Task.spawn(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-closure-optimized-out.html"; + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + let { toolbox, panel, panelWin } = yield openDebugger(); + + let sources = panelWin.DebuggerView.Sources; + yield panel.addBreakpoint({ actor: sources.values[0], line: 18 }); + yield ensureThreadClientState(panel, "resumed"); + + let fetchedScopes = panelWin.once(panelWin.EVENTS.FETCHED_SCOPES); + + // Cause the debuggee to pause + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let button = content.document.querySelector("button"); + button.click(); + }); + + yield fetchedScopes; + ok(true, "Scopes were fetched"); + + yield toolbox.selectTool("webconsole"); + + // This is the meat of the test: evaluate the optimized out variable. + hud.jsterm.execute("upvar"); + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "optimized out", + category: CATEGORY_OUTPUT, + }] + }); + + finishTest(); + }).then(null, aError => { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + }); +} + +// Debugger helper functions stolen from devtools/client/debugger/test/head.js. + +function ensureThreadClientState(aPanel, aState) { + let thread = aPanel.panelWin.gThreadClient; + let state = thread.state; + + info("Thread is: '" + state + "'."); + + if (state == aState) { + return promise.resolve(null); + } + return waitForThreadEvents(aPanel, aState); +} + +function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) { + info("Waiting for thread event: '" + aEventName + "' to fire: " + + aEventRepeat + " time(s)."); + + let deferred = promise.defer(); + let thread = aPanel.panelWin.gThreadClient; + let count = 0; + + thread.addListener(aEventName, function onEvent(eventName, ...args) { + info("Thread event '" + eventName + "' fired: " + (++count) + " time(s)."); + + if (count == aEventRepeat) { + ok(true, "Enough '" + eventName + "' thread events have been fired."); + thread.removeListener(eventName, onEvent); + deferred.resolve.apply(deferred, args); + } + }); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_console_private_browsing.js b/devtools/client/webconsole/test/browser_console_private_browsing.js new file mode 100644 index 000000000..4b3a79329 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_private_browsing.js @@ -0,0 +1,192 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Bug 874061: test for how the browser and web consoles display messages coming +// from private windows. See bug for description of expected behavior. + +"use strict"; + +function test() { + const TEST_URI = "data:text/html;charset=utf8,<p>hello world! bug 874061" + + "<button onclick='console.log(\"foobar bug 874061\");" + + "fooBazBaz.yummy()'>click</button>"; + let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"] + .getService(Ci.nsIConsoleAPIStorage); + let privateWindow, privateBrowser, privateTab, privateContent; + let hud, expectedMessages, nonPrivateMessage; + + // This test is slightly more involved: it opens the web console twice, + // a new private window once, and the browser console twice. We can get + // a timeout with debug builds on slower machines. + requestLongerTimeout(2); + start(); + + function start() { + gBrowser.selectedTab = gBrowser.addTab("data:text/html;charset=utf8," + + "<p>hello world! I am not private!"); + gBrowser.selectedBrowser.addEventListener("load", onLoadTab, true); + } + + function onLoadTab() { + gBrowser.selectedBrowser.removeEventListener("load", onLoadTab, true); + info("onLoadTab()"); + + // Make sure we have a clean state to start with. + Services.console.reset(); + ConsoleAPIStorage.clearEvents(); + + // Add a non-private message to the browser console. + ContentTask.spawn(gBrowser.selectedBrowser, null, function* () { + content.console.log("bug874061-not-private"); + }); + + nonPrivateMessage = { + name: "console message from a non-private window", + text: "bug874061-not-private", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }; + + privateWindow = OpenBrowserWindow({ private: true }); + ok(privateWindow, "new private window"); + ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window's private"); + + whenDelayedStartupFinished(privateWindow, onPrivateWindowReady); + } + + function onPrivateWindowReady() { + info("private browser window opened"); + privateBrowser = privateWindow.gBrowser; + + privateTab = privateBrowser.selectedTab = privateBrowser.addTab(TEST_URI); + privateBrowser.selectedBrowser.addEventListener("load", function onLoad() { + info("private tab opened"); + privateBrowser.selectedBrowser.removeEventListener("load", onLoad, true); + privateContent = privateBrowser.selectedBrowser.contentWindow; + ok(PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser), + "tab window is private"); + openConsole(privateTab).then(consoleOpened); + }, true); + } + + function addMessages() { + let button = privateContent.document.querySelector("button"); + ok(button, "button in page"); + EventUtils.synthesizeMouse(button, 2, 2, {}, privateContent); + } + + function consoleOpened(injectedHud) { + hud = injectedHud; + ok(hud, "web console opened"); + + addMessages(); + expectedMessages = [ + { + name: "script error", + text: "fooBazBaz is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + name: "console message", + text: "foobar bug 874061", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + ]; + + // Make sure messages are displayed in the web console as they happen, even + // if this is a private tab. + waitForMessages({ + webconsole: hud, + messages: expectedMessages, + }).then(testCachedMessages); + } + + function testCachedMessages() { + info("testCachedMessages()"); + closeConsole(privateTab).then(() => { + info("web console closed"); + openConsole(privateTab).then(consoleReopened); + }); + } + + function consoleReopened(injectedHud) { + hud = injectedHud; + ok(hud, "web console reopened"); + + // Make sure that cached messages are displayed in the web console, even + // if this is a private tab. + waitForMessages({ + webconsole: hud, + messages: expectedMessages, + }).then(testBrowserConsole); + } + + function testBrowserConsole() { + info("testBrowserConsole()"); + closeConsole(privateTab).then(() => { + info("web console closed"); + HUDService.toggleBrowserConsole().then(onBrowserConsoleOpen); + }); + } + + // Make sure that the cached messages from private tabs are not displayed in + // the browser console. + function checkNoPrivateMessages() { + let text = hud.outputNode.textContent; + is(text.indexOf("fooBazBaz"), -1, "no exception displayed"); + is(text.indexOf("bug 874061"), -1, "no console message displayed"); + } + + function onBrowserConsoleOpen(injectedHud) { + hud = injectedHud; + ok(hud, "browser console opened"); + + checkNoPrivateMessages(); + addMessages(); + expectedMessages.push(nonPrivateMessage); + + // Make sure that live messages are displayed in the browser console, even + // from private tabs. + waitForMessages({ + webconsole: hud, + messages: expectedMessages, + }).then(testPrivateWindowClose); + } + + function testPrivateWindowClose() { + info("close the private window and check if private messages are removed"); + hud.jsterm.once("private-messages-cleared", () => { + isnot(hud.outputNode.textContent.indexOf("bug874061-not-private"), -1, + "non-private messages are still shown after private window closed"); + checkNoPrivateMessages(); + + info("close the browser console"); + HUDService.toggleBrowserConsole().then(() => { + info("reopen the browser console"); + executeSoon(() => + HUDService.toggleBrowserConsole().then(onBrowserConsoleReopen)); + }); + }); + privateWindow.BrowserTryToCloseWindow(); + } + + function onBrowserConsoleReopen(injectedHud) { + hud = injectedHud; + ok(hud, "browser console reopened"); + + // Make sure that the non-private message is still shown after reopen. + waitForMessages({ + webconsole: hud, + messages: [nonPrivateMessage], + }).then(() => { + // Make sure that no private message is displayed after closing the + // private window and reopening the Browser Console. + checkNoPrivateMessages(); + executeSoon(finishTest); + }); + } +} diff --git a/devtools/client/webconsole/test/browser_console_server_logging.js b/devtools/client/webconsole/test/browser_console_server_logging.js new file mode 100644 index 000000000..eaef12330 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_server_logging.js @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check that server log appears in the console panel - bug 1168872 +add_task(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console-server-logging.sjs"; + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + // Set logging filter and wait till it's set on the backend + hud.setFilterState("serverlog", true); + yield updateServerLoggingListener(hud); + + BrowserReloadSkipCache(); + + // Note that the test is also checking out the (printf like) + // formatters and encoding of UTF8 characters (see the one at the end). + let text = "values: string Object { a: 10 } 123 1.12 \u2713"; + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: text, + category: CATEGORY_SERVER, + severity: SEVERITY_LOG, + }], + }); + // Clean up filter + hud.setFilterState("serverlog", false); + yield updateServerLoggingListener(hud); +}); + +add_task(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console-server-logging-array.sjs"; + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + // Set logging filter and wait till it's set on the backend + hud.setFilterState("serverlog", true); + yield updateServerLoggingListener(hud); + + BrowserReloadSkipCache(); + // Note that the test is also checking out the (printf like) + // formatters and encoding of UTF8 characters (see the one at the end). + let text = "Object { best: \"Firefox\", reckless: \"Chrome\", " + + "new_ie: \"Safari\", new_new_ie: \"Edge\" }"; + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: text, + category: CATEGORY_SERVER, + severity: SEVERITY_LOG, + }], + }); + // Clean up filter + hud.setFilterState("serverlog", false); + yield updateServerLoggingListener(hud); +}); + +function updateServerLoggingListener(hud) { + let deferred = promise.defer(); + hud.ui._updateServerLoggingListener(response => { + deferred.resolve(response); + }); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view.js b/devtools/client/webconsole/test/browser_console_variables_view.js new file mode 100644 index 000000000..ecd8071ce --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view.js @@ -0,0 +1,204 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that variables view works as expected in the web console. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +var hud, gVariablesView; + +registerCleanupFunction(function () { + hud = gVariablesView = null; +}); + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + + let msg = yield hud.jsterm.execute("(function foo(){})"); + + ok(msg, "output message found"); + ok(msg.textContent.includes("function foo()"), + "message text check"); + + executeSoon(() => { + EventUtils.synthesizeMouse(msg.querySelector("a"), 2, 2, {}, hud.iframeWindow); + }); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + ok(varView, "object inspector opened on click"); + + yield findVariableViewProperties(varView, [{ + name: "name", + value: "foo", + }], { webconsole: hud }); +}); + +add_task(function* () { + let msg = yield hud.jsterm.execute("fooObj"); + + ok(msg, "output message found"); + ok(msg.textContent.includes('{ testProp: "testValue" }'), + "message text check"); + + let anchor = msg.querySelector("a"); + ok(anchor, "object link found"); + + let fetched = hud.jsterm.once("variablesview-fetched"); + + // executeSoon + EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow); + + let view = yield fetched; + + let results = yield onFooObjFetch(view); + + let vView = yield onTestPropFound(results); + let results2 = yield onFooObjFetchAfterUpdate(vView); + + let vView2 = yield onUpdatedTestPropFound(results2); + let results3 = yield onFooObjFetchAfterPropRename(vView2); + + let vView3 = yield onRenamedTestPropFound(results3); + let results4 = yield onPropUpdateError(vView3); + + yield onRenamedTestPropFoundAgain(results4); + + let prop = results4[0].matchedProp; + yield testPropDelete(prop); +}); + +function onFooObjFetch(aVar) { + gVariablesView = aVar._variablesView; + ok(gVariablesView, "variables view object"); + + return findVariableViewProperties(aVar, [ + { name: "testProp", value: "testValue" }, + ], { webconsole: hud }); +} + +function onTestPropFound(aResults) { + let prop = aResults[0].matchedProp; + ok(prop, "matched the |testProp| property in the variables view"); + + is("testValue", aResults[0].value, + "|fooObj.testProp| value is correct"); + + // Check that property value updates work and that jsterm functions can be + // used. + return updateVariablesViewProperty({ + property: prop, + field: "value", + string: "document.title + window.location + $('p')", + webconsole: hud + }); +} + +function onFooObjFetchAfterUpdate(aVar) { + info("onFooObjFetchAfterUpdate"); + let expectedValue = content.document.title + content.location + + "[object HTMLParagraphElement]"; + + return findVariableViewProperties(aVar, [ + { name: "testProp", value: expectedValue }, + ], { webconsole: hud }); +} + +function onUpdatedTestPropFound(aResults) { + let prop = aResults[0].matchedProp; + ok(prop, "matched the updated |testProp| property value"); + + is(content.wrappedJSObject.fooObj.testProp, aResults[0].value, + "|fooObj.testProp| value has been updated"); + + // Check that property name updates work. + return updateVariablesViewProperty({ + property: prop, + field: "name", + string: "testUpdatedProp", + webconsole: hud + }); +} + +function* onFooObjFetchAfterPropRename(aVar) { + info("onFooObjFetchAfterPropRename"); + + let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + content.location + para; + }); + + // Check that the new value is in the variables view. + return findVariableViewProperties(aVar, [ + { name: "testUpdatedProp", value: expectedValue }, + ], { webconsole: hud }); +} + +function onRenamedTestPropFound(aResults) { + let prop = aResults[0].matchedProp; + ok(prop, "matched the renamed |testProp| property"); + + ok(!content.wrappedJSObject.fooObj.testProp, + "|fooObj.testProp| has been deleted"); + is(content.wrappedJSObject.fooObj.testUpdatedProp, aResults[0].value, + "|fooObj.testUpdatedProp| is correct"); + + // Check that property value updates that cause exceptions are reported in + // the web console output. + return updateVariablesViewProperty({ + property: prop, + field: "value", + string: "foobarzFailure()", + webconsole: hud + }); +} + +function* onPropUpdateError(aVar) { + info("onPropUpdateError"); + + let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + content.location + para; + }); + + // Make sure the property did not change. + return findVariableViewProperties(aVar, [ + { name: "testUpdatedProp", value: expectedValue }, + ], { webconsole: hud }); +} + +function onRenamedTestPropFoundAgain(aResults) { + let prop = aResults[0].matchedProp; + ok(prop, "matched the renamed |testProp| property again"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + name: "exception in property update reported in the web console output", + text: "foobarzFailure", + category: CATEGORY_OUTPUT, + severity: SEVERITY_ERROR, + }], + }); +} + +function testPropDelete(aProp) { + gVariablesView.window.focus(); + aProp.focus(); + + executeSoon(() => { + EventUtils.synthesizeKey("VK_DELETE", {}, gVariablesView.window); + }); + + return waitForSuccess({ + name: "property deleted", + timeout: 60000, + validator: () => !("testUpdatedProp" in content.wrappedJSObject.fooObj) + }); +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js b/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js new file mode 100644 index 000000000..522fe4754 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js @@ -0,0 +1,59 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that ensures DOM nodes are rendered correctly in VariablesView. + +"use strict"; + +function test() { + const TEST_URI = ` + data:text/html;charset=utf-8, + <html> + <head> + <title>Test for DOM nodes in variables view</title> + </head> + <body> + <div></div> + <div id="testID"></div> + <div class="single-class"></div> + <div class="multiple-classes another-class"></div> + <div class="class-and-id" id="class-and-id"></div> + <div class="multiple-classes-and-id another-class" + id="multiple-classes-and-id"></div> + <div class=" whitespace-start"></div> + <div class="whitespace-end "></div> + <div class="multiple spaces"></div> + </body> + </html> +`; + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + const jsterm = hud.jsterm; + + let deferred = promise.defer(); + jsterm.once("variablesview-fetched", (_, val) => deferred.resolve(val)); + jsterm.execute("inspect(document.querySelectorAll('div'))"); + + let variableScope = yield deferred.promise; + ok(variableScope, "Variables view opened"); + + yield findVariableViewProperties(variableScope, [ + { name: "0", value: "<div>"}, + { name: "1", value: "<div#testID>"}, + { name: "2", value: "<div.single-class>"}, + { name: "3", value: "<div.multiple-classes.another-class>"}, + { name: "4", value: "<div#class-and-id.class-and-id>"}, + { name: "5", value: "<div#multiple-classes-and-id." + + "multiple-classes-and-id.another-class>"}, + { name: "6", value: "<div.whitespace-start>"}, + { name: "7", value: "<div.whitespace-end>"}, + { name: "8", value: "<div.multiple.spaces>"}, + ], { webconsole: hud}); + } +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js b/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js new file mode 100644 index 000000000..ec9ffa7b7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js @@ -0,0 +1,135 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/* Test case that ensures Array and other list types are not sorted in variables + * view. + * + * The tested types are: + * - Array + * - Int8Array + * - Int16Array + * - Int32Array + * - Uint8Array + * - Uint16Array + * - Uint32Array + * - Uint8ClampedArray + * - Float32Array + * - Float64Array + * - NodeList + */ + +function test() { + const TEST_URI = "data:text/html;charset=utf-8, \ + <html> \ + <head> \ + <title>Test document for bug 977500</title> \ + </head> \ + <body> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + <div></div> \ + </body> \ + </html>"; + + let jsterm; + + function* runner() { + const typedArrayTypes = ["Int8Array", "Int16Array", "Int32Array", + "Uint8Array", "Uint16Array", "Uint32Array", + "Uint8ClampedArray", "Float32Array", + "Float64Array"]; + + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + jsterm = hud.jsterm; + + // Create an ArrayBuffer of 80 bytes to test TypedArrays. 80 bytes is + // enough to get 10 items in all different TypedArrays. + yield jsterm.execute("let buf = new ArrayBuffer(80);"); + + // Array + yield testNotSorted("Array(0,1,2,3,4,5,6,7,8,9,10)"); + // NodeList + yield testNotSorted("document.querySelectorAll('div')"); + // Object + yield testSorted("Object({'hello':1,1:5,10:2,4:2,'abc':1})"); + + // Typed arrays. + for (let type of typedArrayTypes) { + yield testNotSorted("new " + type + "(buf)"); + } + } + + /** + * A helper that ensures the properties are not sorted when an object + * specified by aObject is inspected. + * + * @param string aObject + * A string that, once executed, creates and returns the object to + * inspect. + */ + function* testNotSorted(aObject) { + info("Testing " + aObject); + let deferred = promise.defer(); + jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar)); + jsterm.execute("inspect(" + aObject + ")"); + + let variableScope = yield deferred.promise; + ok(variableScope, "Variables view opened"); + + // If the properties are sorted: keys = ["0", "1", "10",...] <- incorrect + // If the properties are not sorted: keys = ["0", "1", "2",...] <- correct + let keyIterator = variableScope._store.keys(); + is(keyIterator.next().value, "0", "First key is 0"); + is(keyIterator.next().value, "1", "Second key is 1"); + + // If the properties are sorted, the next one will be 10. + is(keyIterator.next().value, "2", "Third key is 2, not 10"); + } + /** + * A helper that ensures the properties are sorted when an object + * specified by aObject is inspected. + * + * @param string aObject + * A string that, once executed, creates and returns the object to + * inspect. + */ + function* testSorted(aObject) { + info("Testing " + aObject); + let deferred = promise.defer(); + jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar)); + jsterm.execute("inspect(" + aObject + ")"); + + let variableScope = yield deferred.promise; + ok(variableScope, "Variables view opened"); + + // If the properties are sorted: + // keys = ["1", "4", "10",..., "abc", "hello"] <- correct + // If the properties are not sorted: + // keys = ["1", "10", "4",...] <- incorrect + let keyIterator = variableScope._store.keys(); + is(keyIterator.next().value, "1", "First key should be 1"); + is(keyIterator.next().value, "4", "Second key should be 4"); + + // If the properties are sorted, the next one will be 10. + is(keyIterator.next().value, "10", "Third key is 10"); + // If sorted next properties should be "abc" then "hello" + is(keyIterator.next().value, "abc", "Fourth key is abc"); + is(keyIterator.next().value, "hello", "Fifth key is hello"); + } + + Task.spawn(runner).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view_filter.js b/devtools/client/webconsole/test/browser_console_variables_view_filter.js new file mode 100644 index 000000000..142410839 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_filter.js @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that variables view filter feature works fine in the console. + +function props(view, prefix = "") { + // First match only the visible one, not hidden by a search + let visible = [...view].filter(([id, prop]) => prop._isMatch); + // Then flatten the list into a list of strings + // being the jsonpath of each attribute being visible in the view + return visible.reduce((list, [id, prop]) => { + list.push(prefix + id); + return list.concat(props(prop, prefix + id + ".")); + }, []); +} + +function assertAttrs(view, expected, message) { + is(props(view).join(","), expected, message); +} + +add_task(function* () { + yield loadTab("data:text/html;charset=utf-8,webconsole-filter"); + + let hud = yield openConsole(); + + let jsterm = hud.jsterm; + + let fetched = jsterm.once("variablesview-fetched"); + + yield jsterm.execute("inspect({ foo: { bar : \"baz\" } })"); + + let view = yield fetched; + let variablesView = view._variablesView; + let searchbox = variablesView._searchboxNode; + + assertAttrs(view, "foo,__proto__", + "To start with, we just see the top level foo attr"); + + fetched = jsterm.once("variablesview-fetched"); + searchbox.value = "bar"; + searchbox.doCommand(); + view = yield fetched; + + assertAttrs(view, "", + "If we don't manually expand nested attr, we don't see them"); + + fetched = jsterm.once("variablesview-fetched"); + searchbox.value = ""; + searchbox.doCommand(); + view = yield fetched; + + assertAttrs(view, "foo", + "If we reset the search, we get back to original state"); + + yield [...view][0][1].expand(); + + fetched = jsterm.once("variablesview-fetched"); + searchbox.value = "bar"; + searchbox.doCommand(); + view = yield fetched; + + assertAttrs(view, "foo,foo.bar", "Now if we expand, we see the nested attr"); + + fetched = jsterm.once("variablesview-fetched"); + searchbox.value = "baz"; + searchbox.doCommand(); + view = yield fetched; + + assertAttrs(view, "foo,foo.bar", "We can also search for attr values"); + + fetched = jsterm.once("variablesview-fetched"); + searchbox.value = ""; + searchbox.doCommand(); + view = yield fetched; + + assertAttrs(view, "foo", + "If we reset again, we get back to original state again"); +}); diff --git a/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js b/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js new file mode 100644 index 000000000..c1b2194de --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that variables view is linked to the inspector for highlighting and +// selecting DOM nodes + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-952277-highlight-nodes-in-vview.html"; + +var gWebConsole, gJSTerm, gVariablesView, gToolbox; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(hud => { + consoleOpened(hud); + }); + }); +} + +function consoleOpened(hud) { + gWebConsole = hud; + gJSTerm = hud.jsterm; + gToolbox = gDevTools.getToolbox(hud.target); + gJSTerm.execute("document.querySelectorAll('p')").then(onQSAexecuted); +} + +function onQSAexecuted(msg) { + ok(msg, "output message found"); + let anchor = msg.querySelector("a"); + ok(anchor, "object link found"); + + gJSTerm.once("variablesview-fetched", onNodeListViewFetched); + + executeSoon(() => + EventUtils.synthesizeMouse(anchor, 2, 2, {}, gWebConsole.iframeWindow) + ); +} + +function onNodeListViewFetched(event, variable) { + gVariablesView = variable._variablesView; + ok(gVariablesView, "variables view object"); + + // Transform the vview into an array we can filter properties from + let props = [...variable].map(([id, prop]) => [id, prop]); + + // These properties are the DOM nodes ones + props = props.filter(v => v[0].match(/[0-9]+/)); + + function hoverOverDomNodeVariableAndAssertHighlighter(index) { + if (props[index]) { + let prop = props[index][1]; + + gToolbox.once("node-highlight", () => { + ok(true, "The highlighter was shown on hover of the DOMNode"); + gToolbox.highlighterUtils.unhighlight().then(() => { + clickOnDomNodeVariableAndAssertInspectorSelected(index); + }); + }); + + // Rather than trying to emulate a mouseenter event, let's call the + // variable's highlightDomNode and see if it has the desired effect + prop.highlightDomNode(); + } else { + finishUp(); + } + } + + function clickOnDomNodeVariableAndAssertInspectorSelected(index) { + let prop = props[index][1]; + + // Make sure the inspector is initialized so we can listen to its events + gToolbox.initInspector().then(() => { + // Rather than trying to click on the value here, let's just call the + // variable's openNodeInInspector function and see if it has the + // desired effect + prop.openNodeInInspector().then(() => { + is(gToolbox.currentToolId, "inspector", + "The toolbox switched over the inspector on DOMNode click"); + gToolbox.selectTool("webconsole").then(() => { + hoverOverDomNodeVariableAndAssertHighlighter(index + 1); + }); + }); + }); + } + + hoverOverDomNodeVariableAndAssertHighlighter(0); +} + +function finishUp() { + gWebConsole = gJSTerm = gVariablesView = gToolbox = null; + + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view_special_names.js b/devtools/client/webconsole/test/browser_console_variables_view_special_names.js new file mode 100644 index 000000000..fa0dd4b92 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_special_names.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that variables view handles special names like "<return>" +// properly for ordinary displays. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 1084430"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + ok(hud, "web console opened"); + + hud.setFilterState("log", false); + registerCleanupFunction(() => hud.setFilterState("log", true)); + + hud.jsterm.execute("inspect({ '<return>': 47, '<exception>': 91 })"); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + ok(varView, "variables view object"); + + let props = yield findVariableViewProperties(varView, [ + { name: "<return>", value: 47 }, + { name: "<exception>", value: 91 }, + ], { webconsole: hud }); + + for (let prop of props) { + ok(!prop.matchedProp._internalItem, prop.name + " is not marked internal"); + let target = prop.matchedProp._target; + ok(!target.hasAttribute("pseudo-item"), + prop.name + " is not a pseudo-item"); + } +}); diff --git a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js new file mode 100644 index 000000000..e83a8d626 --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js @@ -0,0 +1,109 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure web console eval happens in the user-selected stackframe +// from the js debugger, when changing the value of a property in the variables +// view. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let dbgPanel = yield openDebugger(); + yield waitForFrameAdded(); + yield openConsole(); + yield testVariablesView(hud); +}); + +function* waitForFrameAdded() { + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + let thread = toolbox.threadClient; + + info("Waiting for framesadded"); + yield new Promise(resolve => { + thread.addOneTimeListener("framesadded", resolve); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.firstCall(); + }); + }); +} + +function* testVariablesView(hud) { + let jsterm = hud.jsterm; + let msg = yield jsterm.execute("fooObj"); + ok(msg, "output message found"); + ok(msg.textContent.includes('{ testProp2: "testValue2" }'), + "message text check"); + + let anchor = msg.querySelector("a"); + ok(anchor, "object link found"); + + info("Waiting for variable view to appear"); + let variable = yield new Promise(resolve => { + jsterm.once("variablesview-fetched", (e, variable) => { + resolve(variable); + }); + executeSoon(() => EventUtils.synthesizeMouse(anchor, 2, 2, {}, + hud.iframeWindow)); + }); + + info("Waiting for findVariableViewProperties"); + let results = yield findVariableViewProperties(variable, [ + { name: "testProp2", value: "testValue2" }, + { name: "testProp", value: "testValue", dontMatch: true }, + ], { webconsole: hud }); + + let prop = results[0].matchedProp; + ok(prop, "matched the |testProp2| property in the variables view"); + + // Check that property value updates work and that jsterm functions can be + // used. + variable = yield updateVariablesViewProperty({ + property: prop, + field: "value", + string: "document.title + foo2 + $('p')", + webconsole: hud + }); + + info("onFooObjFetchAfterUpdate"); + let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + "foo2SecondCall" + para; + }); + + results = yield findVariableViewProperties(variable, [ + { name: "testProp2", value: expectedValue }, + ], { webconsole: hud }); + + prop = results[0].matchedProp; + ok(prop, "matched the updated |testProp2| property value"); + + // Check that testProp2 was updated. + yield new Promise(resolve => { + executeSoon(() => { + jsterm.execute("fooObj.testProp2").then(resolve); + }); + }); + + expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + "foo2SecondCall" + para; + }); + + isnot(hud.outputNode.textContent.indexOf(expectedValue), -1, + "fooObj.testProp2 is correct"); +} diff --git a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js new file mode 100644 index 000000000..556e7275d --- /dev/null +++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js @@ -0,0 +1,112 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure web console eval works while the js debugger paused the +// page, and while the inspector is active. See bug 886137. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let dbgPanel = yield openDebugger(); + yield openInspector(); + yield waitForFrameAdded(); + + yield openConsole(); + yield testVariablesView(hud); +}); + +function* waitForFrameAdded() { + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + let thread = toolbox.threadClient; + + info("Waiting for framesadded"); + yield new Promise(resolve => { + thread.addOneTimeListener("framesadded", resolve); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.firstCall(); + }); + }); +} + +function* testVariablesView(hud) { + info("testVariablesView"); + let jsterm = hud.jsterm; + + let msg = yield jsterm.execute("fooObj"); + ok(msg, "output message found"); + ok(msg.textContent.includes('{ testProp2: "testValue2" }'), + "message text check"); + + let anchor = msg.querySelector("a"); + ok(anchor, "object link found"); + + info("Waiting for variable view to appear"); + let variable = yield new Promise(resolve => { + jsterm.once("variablesview-fetched", (e, variable) => { + resolve(variable); + }); + executeSoon(() => EventUtils.synthesizeMouse(anchor, 2, 2, {}, + hud.iframeWindow)); + }); + + info("Waiting for findVariableViewProperties"); + let results = yield findVariableViewProperties(variable, [ + { name: "testProp2", value: "testValue2" }, + { name: "testProp", value: "testValue", dontMatch: true }, + ], { webconsole: hud }); + + let prop = results[0].matchedProp; + ok(prop, "matched the |testProp2| property in the variables view"); + + // Check that property value updates work and that jsterm functions can be + // used. + variable = yield updateVariablesViewProperty({ + property: prop, + field: "value", + string: "document.title + foo2 + $('p')", + webconsole: hud + }); + + info("onFooObjFetchAfterUpdate"); + let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + "foo2SecondCall" + para; + }); + + results = yield findVariableViewProperties(variable, [ + { name: "testProp2", value: expectedValue }, + ], { webconsole: hud }); + + prop = results[0].matchedProp; + ok(prop, "matched the updated |testProp2| property value"); + + // Check that testProp2 was updated. + yield new Promise(resolve => { + executeSoon(() => { + jsterm.execute("fooObj.testProp2").then(resolve); + }); + }); + + expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let para = content.wrappedJSObject.document.querySelector("p"); + return content.document.title + "foo2SecondCall" + para; + }); + + isnot(hud.outputNode.textContent.indexOf(expectedValue), -1, + "fooObj.testProp2 is correct"); +} diff --git a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js new file mode 100644 index 000000000..bc923ff44 --- /dev/null +++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js @@ -0,0 +1,157 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure web console eval happens in the user-selected stackframe +// from the js debugger. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +var gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController; +var gStackframes; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} + +function consoleOpened(hud) { + gWebConsole = hud; + gJSTerm = hud.jsterm; + gJSTerm.execute("foo").then(onExecuteFoo); +} + +function onExecuteFoo() { + isnot(gWebConsole.outputNode.textContent.indexOf("globalFooBug783499"), -1, + "|foo| value is correct"); + + gJSTerm.clearOutput(); + + // Test for Bug 690529 - Web Console and Scratchpad should evaluate + // expressions in the scope of the content window, not in a sandbox. + executeSoon(() => { + gJSTerm.execute("foo2 = 'newFoo'; window.foo2").then(onNewFoo2); + }); +} + +function onNewFoo2(msg) { + is(gWebConsole.outputNode.textContent.indexOf("undefined"), -1, + "|undefined| is not displayed after adding |foo2|"); + + ok(msg, "output result found"); + + isnot(msg.textContent.indexOf("newFoo"), -1, + "'newFoo' is displayed after adding |foo2|"); + + gJSTerm.clearOutput(); + + info("openDebugger"); + executeSoon(() => openDebugger().then(debuggerOpened)); +} + +function debuggerOpened(aResult) { + gDebuggerWin = aResult.panelWin; + gDebuggerController = gDebuggerWin.DebuggerController; + gThread = gDebuggerController.activeThread; + gStackframes = gDebuggerController.StackFrames; + + info("openConsole"); + executeSoon(() => + openConsole().then(() => + gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2) + ) + ); +} + +function onExecuteFooAndFoo2() { + let expected = "globalFooBug783499newFoo"; + isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, + "|foo + foo2| is displayed after starting the debugger"); + + executeSoon(() => { + gJSTerm.clearOutput(); + + info("openDebugger"); + openDebugger().then(() => { + gThread.addOneTimeListener("framesadded", onFramesAdded); + + info("firstCall()"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.firstCall(); + }); + }); + }); +} + +function onFramesAdded() { + info("onFramesAdded, openConsole() now"); + executeSoon(() => + openConsole().then(() => + gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2InSecondCall) + ) + ); +} + +function onExecuteFooAndFoo2InSecondCall() { + let expected = "globalFooBug783499foo2SecondCall"; + isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, + "|foo + foo2| from |secondCall()|"); + + function runOpenConsole() { + openConsole().then(() => { + gJSTerm.execute("foo + foo2 + foo3").then(onExecuteFoo23InFirstCall); + }); + } + + executeSoon(() => { + gJSTerm.clearOutput(); + + info("openDebugger and selectFrame(1)"); + + openDebugger().then(() => { + gStackframes.selectFrame(1); + + info("openConsole"); + executeSoon(() => runOpenConsole()); + }); + }); +} + +function onExecuteFoo23InFirstCall() { + let expected = "fooFirstCallnewFoofoo3FirstCall"; + isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, + "|foo + foo2 + foo3| from |firstCall()|"); + + executeSoon(() => + gJSTerm.execute("foo = 'abba'; foo3 = 'bug783499'; foo + foo3").then( + onExecuteFooAndFoo3ChangesInFirstCall)); +} + +var onExecuteFooAndFoo3ChangesInFirstCall = Task.async(function*() { + let expected = "abbabug783499"; + isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1, + "|foo + foo3| updated in |firstCall()|"); + + yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { + is(content.wrappedJSObject.foo, "globalFooBug783499", + "|foo| in content window"); + is(content.wrappedJSObject.foo2, "newFoo", "|foo2| in content window"); + ok(!content.wrappedJSObject.foo3, + "|foo3| was not added to the content window"); + }); + + gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController = + gStackframes = null; + executeSoon(finishTest); +}); diff --git a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js new file mode 100644 index 000000000..bc116d443 --- /dev/null +++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js @@ -0,0 +1,71 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test to make sure that web console commands can fire while paused at a +// breakpoint that was triggered from a JS call. Relies on asynchronous js +// evaluation over the protocol - see Bug 1088861. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-eval-in-stackframe.html"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +add_task(function* () { + yield loadTab(TEST_URI); + + info("open the web console"); + let hud = yield openConsole(); + let {jsterm} = hud; + + info("open the debugger"); + let {panelWin} = yield openDebugger(); + let {DebuggerController} = panelWin; + let {activeThread} = DebuggerController; + + let firstCall = promise.defer(); + let frameAdded = promise.defer(); + executeSoon(() => { + info("Executing firstCall"); + activeThread.addOneTimeListener("framesadded", () => { + executeSoon(frameAdded.resolve); + }); + jsterm.execute("firstCall()").then(firstCall.resolve); + }); + + info("Waiting for a frame to be added"); + yield frameAdded.promise; + + info("Executing basic command while paused"); + yield executeAndConfirm(jsterm, "1 + 2", "3"); + + info("Executing command using scoped variables while paused"); + yield executeAndConfirm(jsterm, "foo + foo2", + '"globalFooBug783499foo2SecondCall"'); + + info("Resuming the thread"); + activeThread.resume(); + + info("Checking the first command, which is the last to resolve since it " + + "paused"); + let node = yield firstCall.promise; + is(node.querySelector(".message-body").textContent, + "undefined", + "firstCall() returned correct value"); +}); + +function* executeAndConfirm(jsterm, input, output) { + info("Executing command `" + input + "`"); + + let node = yield jsterm.execute(input); + + is(node.querySelector(".message-body").textContent, output, + "Expected result from call to " + input); +} diff --git a/devtools/client/webconsole/test/browser_jsterm_inspect.js b/devtools/client/webconsole/test/browser_jsterm_inspect.js new file mode 100644 index 000000000..aa18cbff6 --- /dev/null +++ b/devtools/client/webconsole/test/browser_jsterm_inspect.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the inspect() jsterm helper function works. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>hello bug 869981"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + let jsterm = hud.jsterm; + + /* Check that the window object is inspected */ + jsterm.execute("testProp = 'testValue'"); + + let updated = jsterm.once("variablesview-updated"); + jsterm.execute("inspect(window)"); + let view = yield updated; + ok(view, "variables view object"); + + // The single variable view contains a scope with the variable name + // and unnamed subitem that contains the properties + let variable = view.getScopeAtIndex(0).get(undefined); + ok(variable, "variable object"); + + yield findVariableViewProperties(variable, [ + { name: "testProp", value: "testValue" }, + { name: "document", value: /HTMLDocument \u2192 data:/ }, + ], { webconsole: hud }); + + /* Check that a primitive value can be inspected, too */ + let updated2 = jsterm.once("variablesview-updated"); + jsterm.execute("inspect(1)"); + let view2 = yield updated2; + ok(view2, "variables view object"); + + // Check the label of the scope - it should contain the value + let scope = view.getScopeAtIndex(0); + ok(scope, "variable object"); + + is(scope.name, "1", "The value of the primitive var is correct"); +}); diff --git a/devtools/client/webconsole/test/browser_longstring_hang.js b/devtools/client/webconsole/test/browser_longstring_hang.js new file mode 100644 index 000000000..036ad6e88 --- /dev/null +++ b/devtools/client/webconsole/test/browser_longstring_hang.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that very long strings do not hang the browser. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-859170-longstring-hang.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + info("wait for the initial long string"); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "find 'foobar', no 'foobaz', in long string output", + text: "foobar", + noText: "foobaz", + category: CATEGORY_WEBDEV, + longString: true, + }, + ], + }); + + let clickable = results[0].longStrings[0]; + ok(clickable, "long string ellipsis is shown"); + clickable.scrollIntoView(false); + + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + + info("wait for long string expansion"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "find 'foobaz' after expand, but no 'boom!' at the end", + text: "foobaz", + noText: "boom!", + category: CATEGORY_WEBDEV, + longString: false, + }, + { + text: "too long to be displayed", + longString: false, + }, + ], + }); +}); diff --git a/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js b/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js new file mode 100644 index 000000000..f6f057549 --- /dev/null +++ b/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js @@ -0,0 +1,74 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Test that the netmonitor " + + "displays requests that have been recorded in the " + + "web console, even if the netmonitor hadn't opened yet."; + +const TEST_FILE = "test-network-request.html"; +const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/" + + "test/" + TEST_FILE; + +const NET_PREF = "devtools.webconsole.filter.networkinfo"; +Services.prefs.setBoolPref(NET_PREF, true); +registerCleanupFunction(() => { + Services.prefs.clearUserPref(NET_PREF); +}); + +add_task(function* () { + let { tab, browser } = yield loadTab(TEST_URI); + + // Test that the request appears in the console. + let hud = yield openConsole(); + info("Web console is open"); + + yield loadDocument(browser); + info("Document loaded."); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "network message", + text: TEST_FILE, + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG + } + ] + }); + + // Test that the request appears in the network panel. + let target = TargetFactory.forTab(tab); + let toolbox = yield gDevTools.showToolbox(target, "netmonitor"); + info("Network panel is open."); + + testNetmonitor(toolbox); +}); + +function loadDocument(browser) { + let deferred = promise.defer(); + + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + deferred.resolve(); + }, true); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH); + + return deferred.promise; +} + +function testNetmonitor(toolbox) { + let monitor = toolbox.getCurrentPanel(); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + RequestsMenu.lazyUpdate = false; + + is(RequestsMenu.itemCount, 1, "Network request appears in the network panel"); + + let item = RequestsMenu.getItemAtIndex(0); + is(item.attachment.method, "GET", "The attached method is correct."); + is(item.attachment.url, TEST_PATH, "The attached url is correct."); +} diff --git a/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js b/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js new file mode 100644 index 000000000..38a5b5419 --- /dev/null +++ b/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Make sure that the Web Console output does not break after we try to call +// console.dir() for objects that are not inspectable. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,test for bug 773466"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(true); + + hud.jsterm.execute("console.log('fooBug773466a')"); + hud.jsterm.execute("myObj = Object.create(null)"); + hud.jsterm.execute("console.dir(myObj)"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "fooBug773466a", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + name: "console.dir output", + consoleDir: "[object Object]", + }], + }); + + content.console.log("fooBug773466b"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "fooBug773466b", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_output_longstring_expand.js b/devtools/client/webconsole/test/browser_output_longstring_expand.js new file mode 100644 index 000000000..bae8ca128 --- /dev/null +++ b/devtools/client/webconsole/test/browser_output_longstring_expand.js @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that long strings can be expanded in the console output. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,test for bug 787981 - check " + + "that long strings can be expanded in the output."; + +add_task(function* () { + let { DebuggerServer } = require("devtools/server/main"); + + let longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)) + .join("a") + "foobar"; + let initialString = + longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(true); + hud.jsterm.execute("console.log('bazbaz', '" + longString + "', 'boom')"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log output", + text: ["bazbaz", "boom", initialString], + noText: "foobar", + longString: true, + }], + }); + + let clickable = result.longStrings[0]; + ok(clickable, "long string ellipsis is shown"); + + clickable.scrollIntoView(false); + + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "full string", + text: ["bazbaz", "boom", longString], + category: CATEGORY_WEBDEV, + longString: false, + }], + }); + + hud.jsterm.clearOutput(true); + let msg = yield execute(hud, "'" + longString + "'"); + + isnot(msg.textContent.indexOf(initialString), -1, + "initial string is shown"); + is(msg.textContent.indexOf(longString), -1, + "full string is not shown"); + + clickable = msg.querySelector(".longStringEllipsis"); + ok(clickable, "long string ellipsis is shown"); + + clickable.scrollIntoView(false); + + EventUtils.synthesizeMouse(clickable, 3, 4, {}, hud.iframeWindow); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "full string", + text: longString, + category: CATEGORY_OUTPUT, + longString: false, + }], + }); +}); + +function execute(hud, str) { + let deferred = promise.defer(); + hud.jsterm.execute(str, deferred.resolve); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js b/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js new file mode 100644 index 000000000..36b13ce02 --- /dev/null +++ b/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js @@ -0,0 +1,178 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure messages are not considered repeated when coming from +// different lines of code, or from different severities, etc. +// See bugs 720180 and 800510. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-repeated-messages.html"; +const PREF = "devtools.webconsole.persistlog"; + +add_task(function* () { + Services.prefs.setBoolPref(PREF, true); + + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield consoleOpened(hud); + + let loaded = loadBrowser(browser); + BrowserReload(); + yield loaded; + + yield testCSSRepeats(hud); + yield testCSSRepeatsAfterReload(hud); + yield testConsoleRepeats(hud); + yield testConsoleFalsyValues(hud); + + Services.prefs.clearUserPref(PREF); +}); + +function consoleOpened(hud) { + // Check that css warnings are not coalesced if they come from different + // lines. + info("waiting for 2 css warnings"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + name: "two css warnings", + category: CATEGORY_CSS, + count: 2, + repeats: 1, + }], + }); +} + +function testCSSRepeats(hud) { + info("wait for repeats after page reload"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + name: "two css warnings, repeated twice", + category: CATEGORY_CSS, + repeats: 2, + count: 2, + }], + }); +} + +function testCSSRepeatsAfterReload(hud) { + hud.jsterm.clearOutput(true); + hud.jsterm.execute("testConsole()"); + + info("wait for repeats with the console API"); + + return waitForMessages({ + webconsole: hud, + messages: [ + { + name: "console.log 'foo repeat' repeated twice", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 2, + }, + { + name: "console.log 'foo repeat' repeated once", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 1, + }, + { + name: "console.error 'foo repeat' repeated once", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + repeats: 1, + }, + ], + }); +} + +function testConsoleRepeats(hud) { + hud.jsterm.clearOutput(true); + hud.jsterm.execute("undefined"); + + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.console.log("undefined"); + }); + + info("make sure console API messages are not coalesced with jsterm output"); + + return waitForMessages({ + webconsole: hud, + messages: [ + { + name: "'undefined' jsterm input message", + text: "undefined", + category: CATEGORY_INPUT, + }, + { + name: "'undefined' jsterm output message", + text: "undefined", + category: CATEGORY_OUTPUT, + }, + { + name: "'undefined' console.log message", + text: "undefined", + category: CATEGORY_WEBDEV, + repeats: 1, + }, + ], + }); +} + +function testConsoleFalsyValues(hud) { + hud.jsterm.clearOutput(true); + hud.jsterm.execute("testConsoleFalsyValues()"); + + info("wait for repeats of falsy values with the console API"); + + return waitForMessages({ + webconsole: hud, + messages: [ + { + name: "console.log 'NaN' repeated once", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 1, + }, + { + name: "console.log 'undefined' repeated once", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 1, + }, + { + name: "console.log 'null' repeated once", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 1, + }, + { + name: "console.log 'NaN' repeated twice", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 2, + }, + { + name: "console.log 'undefined' repeated twice", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 2, + }, + { + name: "console.log 'null' repeated twice", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 2, + }, + ], + }); +} diff --git a/devtools/client/webconsole/test/browser_result_format_as_string.js b/devtools/client/webconsole/test/browser_result_format_as_string.js new file mode 100644 index 000000000..0352d0afa --- /dev/null +++ b/devtools/client/webconsole/test/browser_result_format_as_string.js @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Make sure that JS eval result are properly formatted as strings. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-result-format-as-string.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(true); + + let msg = yield execute(hud, "document.querySelector('p')"); + + is(hud.outputNode.textContent.indexOf("bug772506_content"), -1, + "no content element found"); + ok(!hud.outputNode.querySelector("#foobar"), "no #foobar element found"); + + ok(msg, "eval output node found"); + is(msg.textContent.indexOf("<div>"), -1, + "<div> string is not displayed"); + isnot(msg.textContent.indexOf("<p>"), -1, + "<p> string is displayed"); + + EventUtils.synthesizeMouseAtCenter(msg, {type: "mousemove"}); + ok(!gBrowser._bug772506, "no content variable"); +}); + +function execute(hud, str) { + let deferred = promise.defer(); + hud.jsterm.execute(str, deferred.resolve); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js b/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js new file mode 100644 index 000000000..0eeb6eaa3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js @@ -0,0 +1,86 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_REPLACED_API_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-console-replaced-api.html"; +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/testscript.js"; +const PREF = "devtools.webconsole.persistlog"; + +add_task(function* () { + Services.prefs.setBoolPref(PREF, true); + + let { browser } = yield loadTab(TEST_URI); + let hud = yield openConsole(); + + yield testWarningNotPresent(hud); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_REPLACED_API_URI); + yield loaded; + + let hud2 = yield openConsole(); + + yield testWarningPresent(hud2); + + Services.prefs.clearUserPref(PREF); +}); + +function testWarningNotPresent(hud) { + let deferred = promise.defer(); + + is(hud.outputNode.textContent.indexOf("logging API"), -1, + "no warning displayed"); + + // Bug 862024: make sure the warning doesn't show after page reload. + info("reload " + TEST_URI); + executeSoon(function () { + let browser = gBrowser.selectedBrowser; + ContentTask.spawn(browser, null, "() => content.location.reload()"); + }); + + waitForMessages({ + webconsole: hud, + messages: [{ + text: "testscript.js", + category: CATEGORY_NETWORK, + }], + }).then(() => executeSoon(() => { + is(hud.outputNode.textContent.indexOf("logging API"), -1, + "no warning displayed"); + closeConsole().then(deferred.resolve); + })); + + return deferred.promise; +} + +function testWarningPresent(hud) { + info("wait for the warning to show"); + let deferred = promise.defer(); + + let warning = { + webconsole: hud, + messages: [{ + text: /logging API .+ disabled by a script/, + category: CATEGORY_JS, + severity: SEVERITY_WARNING, + }], + }; + + waitForMessages(warning).then(() => { + hud.jsterm.clearOutput(); + + executeSoon(() => { + info("reload the test page and wait for the warning to show"); + waitForMessages(warning).then(deferred.resolve); + let browser = gBrowser.selectedBrowser; + ContentTask.spawn(browser, null, "() => content.location.reload()"); + }); + }); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js new file mode 100644 index 000000000..07f6372d0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js @@ -0,0 +1,69 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// The test loads a web page with mixed active and display content +// on it while the "block mixed content" settings are _off_. +// It then checks that the loading mixed content warning messages +// are logged to the console and have the correct "Learn More" +// url appended to them. +// Bug 875456 - Log mixed content messages from the Mixed Content +// Blocker to the Security Pane in the Web Console + +"use strict"; + +const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" + + "test/test-mixedcontent-securityerrors.html"; +const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + + "Mixed_content" + DOCS_GA_PARAMS; + +add_task(function* () { + yield pushPrefEnv(); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Logged mixed active content", + text: "Loading mixed (insecure) active content " + + "\u201chttp://example.com/\u201d on a secure page", + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + { + name: "Logged mixed passive content - image", + text: "Loading mixed (insecure) display content " + + "\u201chttp://example.com/tests/image/test/mochitest/blue.png\u201d " + + "on a secure page", + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + ], + }); + + yield testClickOpenNewTab(hud, results); +}); + +function pushPrefEnv() { + let deferred = promise.defer(); + let options = {"set": + [["security.mixed_content.block_active_content", false], + ["security.mixed_content.block_display_content", false] + ]}; + SpecialPowers.pushPrefEnv(options, deferred.resolve); + return deferred.promise; +} + +function testClickOpenNewTab(hud, results) { + let warningNode = results[0].clickableElements[0]; + ok(warningNode, "link element"); + ok(warningNode.classList.contains("learn-more-link"), "link class name"); + return simulateMessageLinkClick(warningNode, LEARN_MORE_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_assert.js b/devtools/client/webconsole/test/browser_webconsole_assert.js new file mode 100644 index 000000000..7fc9693f1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_assert.js @@ -0,0 +1,56 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that console.assert() works as expected (i.e. outputs only on falsy +// asserts). See bug 760193. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-assert.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + yield consoleOpened(hud); +}); + +function consoleOpened(hud) { + hud.jsterm.execute("test()"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "undefined", + category: CATEGORY_OUTPUT, + severity: SEVERITY_LOG, + }, + { + text: "start", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "false assert", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + }, + { + text: "falsy assert", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + }, + { + text: "end", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + let nodes = hud.outputNode.querySelectorAll(".message"); + is(nodes.length, 6, + "only six messages are displayed, no output from the true assert"); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js new file mode 100644 index 000000000..0ba168078 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that properties starting with underscores or dollars can be +// autocompleted (bug 967468). + +add_task(function* () { + const TEST_URI = "data:text/html;charset=utf8,test autocompletion with " + + "$ or _"; + yield loadTab(TEST_URI); + + function* autocomplete(term) { + let deferred = promise.defer(); + + jsterm.setInputValue(term); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, deferred.resolve); + + yield deferred.promise; + + ok(popup.itemCount > 0, + "There's " + popup.itemCount + " suggestions for '" + term + "'"); + } + + let { jsterm } = yield openConsole(); + let popup = jsterm.autocompletePopup; + + yield jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}"); + + // Should work with bug 967468. + yield autocomplete("Object.__d"); + yield autocomplete("testObject.$$a"); + + // Here's when things go wrong in bug 967468. + yield autocomplete("Object.__de"); + yield autocomplete("testObject.$$aa"); + + // Should work with bug 1207868. + yield jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};"); + yield autocomplete("foobar"); + yield autocomplete("blargh"); + yield autocomplete("foobar.a"); + yield autocomplete("blargh.a"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js new file mode 100644 index 000000000..bcd2e22d0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js @@ -0,0 +1,60 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the autocomplete input is being blurred and focused when selecting a value. +// This will help screen-readers notify users of the value that was set in the input. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test code completion"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + + info("Type 'd' to open the autocomplete popup"); + yield autocomplete(jsterm, "d"); + + // Add listeners for focus and blur events. + let wasBlurred = false; + input.addEventListener("blur", () => { + wasBlurred = true; + }, { + once: true + }); + + let wasFocused = false; + input.addEventListener("blur", () => { + ok(wasBlurred, "jsterm input received a blur event before received back the focus"); + wasFocused = true; + }, { + once: true + }); + + info("Close the autocomplete popup by simulating a TAB key event"); + let onPopupClosed = jsterm.autocompletePopup.once("popup-closed"); + EventUtils.synthesizeKey("VK_TAB", {}); + + info("Wait for the autocomplete popup to be closed"); + yield onPopupClosed; + + ok(wasFocused, "jsterm input received a focus event"); +}); + +function* autocomplete(jsterm, value) { + let popup = jsterm.autocompletePopup; + + yield new Promise(resolve => { + jsterm.setInputValue(value); + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + ok(popup.isOpen && popup.itemCount > 0, + "Autocomplete popup is open and contains suggestions"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js new file mode 100644 index 000000000..d0c6eb673 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js @@ -0,0 +1,130 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642615"; + +XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper", + "@mozilla.org/widget/clipboardhelper;1", + "nsIClipboardHelper"); +var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield consoleOpened(hud); +}); + +function consoleOpened(HUD) { + let deferred = promise.defer(); + + let jsterm = HUD.jsterm; + let stringToCopy = "foobazbarBug642615"; + + jsterm.clearOutput(); + + ok(!jsterm.completeNode.value, "no completeNode.value"); + + jsterm.setInputValue("doc"); + + let completionValue; + + // wait for key "u" + function onCompletionValue() { + completionValue = jsterm.completeNode.value; + + // Arguments: expected, setup, success, failure. + waitForClipboard( + stringToCopy, + function () { + clipboardHelper.copyString(stringToCopy); + }, + onClipboardCopy, + finishTest); + } + + function onClipboardCopy() { + testSelfXss(); + + jsterm.setInputValue("docu"); + info("wait for completion update after clipboard paste"); + updateEditUIVisibility(); + jsterm.once("autocomplete-updated", onClipboardPaste); + goDoCommand("cmd_paste"); + } + + // Self xss prevention tests (bug 994134) + function testSelfXss() { + info("Self-xss paste tests"); + WebConsoleUtils.usageCount = 0; + is(WebConsoleUtils.usageCount, 0, "Test for usage count getter"); + // Input some commands to check if usage counting is working + for (let i = 0; i <= 3; i++) { + jsterm.setInputValue(i); + jsterm.execute(); + } + is(WebConsoleUtils.usageCount, 4, "Usage count incremented"); + WebConsoleUtils.usageCount = 0; + updateEditUIVisibility(); + + let oldVal = jsterm.getInputValue(); + goDoCommand("cmd_paste"); + let notificationbox = jsterm.hud.document.getElementById("webconsole-notificationbox"); + let notification = notificationbox.getNotificationWithValue("selfxss-notification"); + ok(notification, "Self-xss notification shown"); + is(oldVal, jsterm.getInputValue(), "Paste blocked by self-xss prevention"); + + // Allow pasting + jsterm.setInputValue("allow pasting"); + let evt = document.createEvent("KeyboardEvent"); + evt.initKeyEvent("keyup", true, true, window, + 0, 0, 0, 0, + 0, " ".charCodeAt(0)); + jsterm.inputNode.dispatchEvent(evt); + jsterm.setInputValue(""); + goDoCommand("cmd_paste"); + isnot("", jsterm.getInputValue(), "Paste works"); + } + function onClipboardPaste() { + ok(!jsterm.completeNode.value, "no completion value after paste"); + + info("wait for completion update after undo"); + jsterm.once("autocomplete-updated", onCompletionValueAfterUndo); + + // Get out of the webconsole event loop. + executeSoon(() => { + goDoCommand("cmd_undo"); + }); + } + + function onCompletionValueAfterUndo() { + is(jsterm.completeNode.value, completionValue, + "same completeNode.value after undo"); + + info("wait for completion update after clipboard paste (ctrl-v)"); + jsterm.once("autocomplete-updated", () => { + ok(!jsterm.completeNode.value, + "no completion value after paste (ctrl-v)"); + + // using executeSoon() to get out of the webconsole event loop. + executeSoon(deferred.resolve); + }); + + // Get out of the webconsole event loop. + executeSoon(() => { + EventUtils.synthesizeKey("v", {accelKey: true}); + }); + } + + info("wait for completion value after typing 'docu'"); + jsterm.once("autocomplete-updated", onCompletionValue); + + EventUtils.synthesizeKey("u", {}); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js new file mode 100644 index 000000000..b4471bd6b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that autocomplete doesn't break when trying to reach into objects from +// a different domain, bug 989025. + +"use strict"; + +function test() { + let hud; + + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-989025-iframe-parent.html"; + + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + hud.jsterm.execute("document.title"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "989025 - iframe parent", + category: CATEGORY_OUTPUT, + }], + }); + + let autocompleteUpdated = hud.jsterm.once("autocomplete-updated"); + + hud.jsterm.setInputValue("window[0].document"); + executeSoon(() => { + EventUtils.synthesizeKey(".", {}); + }); + + yield autocompleteUpdated; + + hud.jsterm.setInputValue("window[0].document.title"); + EventUtils.synthesizeKey("VK_RETURN", {}); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Permission denied", + category: CATEGORY_OUTPUT, + severity: SEVERITY_ERROR, + }], + }); + + hud.jsterm.execute("window.location"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test-bug-989025-iframe-parent.html", + category: CATEGORY_OUTPUT, + }], + }); + + yield closeConsole(tab); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js new file mode 100644 index 000000000..60ba5ff0e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js @@ -0,0 +1,245 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that makes sure web console autocomplete happens in the user-selected +// stackframe from the js debugger. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-autocomplete-in-stackframe.html"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +var gStackframes; +registerCleanupFunction(function () { + gStackframes = null; +}); + +requestLongerTimeout(2); +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + yield testCompletion(hud); +}); + +function* testCompletion(hud) { + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + let popup = jsterm.autocompletePopup; + + // Test that document.title gives string methods. Native getters must execute. + input.value = "document.title."; + input.setSelectionRange(input.value.length, input.value.length); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + let newItems = popup.getItems(); + ok(newItems.length > 0, "'document.title.' gave a list of suggestions"); + ok(newItems.some(function (item) { + return item.label == "substr"; + }), "autocomplete results do contain substr"); + ok(newItems.some(function (item) { + return item.label == "toLowerCase"; + }), "autocomplete results do contain toLowerCase"); + ok(newItems.some(function (item) { + return item.label == "strike"; + }), "autocomplete results do contain strike"); + + // Test if 'f' gives 'foo1' but not 'foo2' or 'foo3' + input.value = "f"; + input.setSelectionRange(1, 1); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function (item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function (item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function (item) { + return item.label != "foo2"; + }), "autocomplete results do not contain foo2"); + ok(newItems.every(function (item) { + return item.label != "foo2Obj"; + }), "autocomplete results do not contain foo2Obj"); + ok(newItems.every(function (item) { + return item.label != "foo3"; + }), "autocomplete results do not contain foo3"); + ok(newItems.every(function (item) { + return item.label != "foo3Obj"; + }), "autocomplete results do not contain foo3Obj"); + + // Test if 'foo1Obj.' gives 'prop1' and 'prop2' + input.value = "foo1Obj."; + input.setSelectionRange(8, 8); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "prop1"; + }), "autocomplete results do contain prop1"); + ok(!newItems.every(function (item) { + return item.label != "prop2"; + }), "autocomplete results do contain prop2"); + + // Test if 'foo1Obj.prop2.' gives 'prop21' + input.value = "foo1Obj.prop2."; + input.setSelectionRange(14, 14); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "prop21"; + }), "autocomplete results do contain prop21"); + + info("Opening Debugger"); + let dbg = yield openDebugger(); + + info("Waiting for pause"); + yield pauseDebugger(dbg); + + info("Opening Console again"); + yield openConsole(); + + // From this point on the + // Test if 'f' gives 'foo3' and 'foo1' but not 'foo2' + input.value = "f"; + input.setSelectionRange(1, 1); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function (item) { + return item.label != "foo3"; + }), "autocomplete results do contain foo3"); + ok(!newItems.every(function (item) { + return item.label != "foo3Obj"; + }), "autocomplete results do contain foo3Obj"); + ok(!newItems.every(function (item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function (item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function (item) { + return item.label != "foo2"; + }), "autocomplete results do not contain foo2"); + ok(newItems.every(function (item) { + return item.label != "foo2Obj"; + }), "autocomplete results do not contain foo2Obj"); + + yield openDebugger(); + + gStackframes.selectFrame(1); + + info("openConsole"); + yield openConsole(); + + // Test if 'f' gives 'foo2' and 'foo1' but not 'foo3' + input.value = "f"; + input.setSelectionRange(1, 1); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(newItems.length > 0, "'f' gave a list of suggestions"); + ok(!newItems.every(function (item) { + return item.label != "foo2"; + }), "autocomplete results do contain foo2"); + ok(!newItems.every(function (item) { + return item.label != "foo2Obj"; + }), "autocomplete results do contain foo2Obj"); + ok(!newItems.every(function (item) { + return item.label != "foo1"; + }), "autocomplete results do contain foo1"); + ok(!newItems.every(function (item) { + return item.label != "foo1Obj"; + }), "autocomplete results do contain foo1Obj"); + ok(newItems.every(function (item) { + return item.label != "foo3"; + }), "autocomplete results do not contain foo3"); + ok(newItems.every(function (item) { + return item.label != "foo3Obj"; + }), "autocomplete results do not contain foo3Obj"); + + // Test if 'foo2Obj.' gives 'prop1' + input.value = "foo2Obj."; + input.setSelectionRange(8, 8); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "prop1"; + }), "autocomplete results do contain prop1"); + + // Test if 'foo2Obj.prop1.' gives 'prop11' + input.value = "foo2Obj.prop1."; + input.setSelectionRange(14, 14); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "prop11"; + }), "autocomplete results do contain prop11"); + + // Test if 'foo2Obj.prop1.prop11.' gives suggestions for a string + // i.e. 'length' + input.value = "foo2Obj.prop1.prop11."; + input.setSelectionRange(21, 21); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "length"; + }), "autocomplete results do contain length"); + + // Test if 'foo1Obj[0].' throws no errors. + input.value = "foo2Obj[0]."; + input.setSelectionRange(11, 11); + yield new Promise(resolve => { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve); + }); + + newItems = popup.getItems(); + is(newItems.length, 0, "no items for foo2Obj[0]"); +} + +function pauseDebugger(aResult) { + let debuggerWin = aResult.panelWin; + let debuggerController = debuggerWin.DebuggerController; + let thread = debuggerController.activeThread; + gStackframes = debuggerController.StackFrames; + return new Promise(resolve => { + thread.addOneTimeListener("framesadded", resolve); + + info("firstCall()"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.firstCall(); + }); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js new file mode 100644 index 000000000..afa3dd55d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js @@ -0,0 +1,27 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the autocomplete popup closes on switching tabs. See bug 900448. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete " + + "popup closes on tab switch"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + let popup = hud.jsterm.autocompletePopup; + let popupShown = once(popup, "popup-opened"); + + hud.jsterm.setInputValue("sc"); + EventUtils.synthesizeKey("r", {}); + + yield popupShown; + + yield loadTab("data:text/html;charset=utf-8,<p>testing autocomplete closes"); + + ok(!popup.isOpen, "Popup closes on tab switch"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js new file mode 100644 index 000000000..ff4157a3b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js @@ -0,0 +1,110 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// The test loads a web page with mixed active and display content +// on it while the "block mixed content" settings are _on_. +// It then checks that the blocked mixed content warning messages +// are logged to the console and have the correct "Learn More" +// url appended to them. After the first test finishes, it invokes +// a second test that overrides the mixed content blocker settings +// by clicking on the doorhanger shield and validates that the +// appropriate messages are logged to console. +// Bug 875456 - Log mixed content messages from the Mixed Content +// Blocker to the Security Pane in the Web Console + +"use strict"; + +const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" + + "test/test-mixedcontent-securityerrors.html"; +const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + + "Mixed_content" + DOCS_GA_PARAMS; + +add_task(function* () { + yield pushPrefEnv(); + + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Logged blocking mixed active content", + text: "Blocked loading mixed active content \u201chttp://example.com/\u201d", + category: CATEGORY_SECURITY, + severity: SEVERITY_ERROR, + objects: true, + }, + { + name: "Logged blocking mixed passive content - image", + text: "Blocked loading mixed active content \u201chttp://example.com/\u201d", + category: CATEGORY_SECURITY, + severity: SEVERITY_ERROR, + objects: true, + }, + ], + }); + + yield testClickOpenNewTab(hud, results[0]); + + let results2 = yield mixedContentOverrideTest2(hud, browser); + + yield testClickOpenNewTab(hud, results2[0]); +}); + +function pushPrefEnv() { + let deferred = promise.defer(); + let options = { + "set": [ + ["security.mixed_content.block_active_content", true], + ["security.mixed_content.block_display_content", true], + ["security.mixed_content.use_hsts", false], + ["security.mixed_content.send_hsts_priming", false], + ] + }; + SpecialPowers.pushPrefEnv(options, deferred.resolve); + return deferred.promise; +} + +function mixedContentOverrideTest2(hud, browser) { + let deferred = promise.defer(); + let {gIdentityHandler} = browser.ownerGlobal; + ok(gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"), + "Mixed Active Content state appeared on identity box"); + gIdentityHandler.disableMixedContentProtection(); + + waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Logged blocking mixed active content", + text: "Loading mixed (insecure) active content " + + "\u201chttp://example.com/\u201d on a secure page", + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + { + name: "Logged blocking mixed passive content - image", + text: "Loading mixed (insecure) display content" + + " \u201chttp://example.com/tests/image/test/mochitest/blue.png\u201d" + + " on a secure page", + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + ], + }).then(msgs => deferred.resolve(msgs), e => console.error(e)); + + return deferred.promise; +} + +function testClickOpenNewTab(hud, match) { + let warningNode = match.clickableElements[0]; + ok(warningNode, "link element"); + ok(warningNode.classList.contains("learn-more-link"), "link class name"); + return simulateMessageLinkClick(warningNode, LEARN_MORE_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js b/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js new file mode 100644 index 000000000..ee141a72f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js @@ -0,0 +1,45 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab("data:text/html;charset=utf8,<title>Test for " + + "Bug 1006027"); + + const hud = yield openConsole(tab); + + hud.jsterm.execute("console.log('bug1006027')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log", + text: "bug1006027", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + info("hud.outputNode.textContent:\n" + hud.outputNode.textContent); + let timestampNodes = hud.outputNode.querySelectorAll("span.timestamp"); + let aTimestampMilliseconds = Array.prototype.map.call(timestampNodes, + function (value) { + // We are parsing timestamps as local time, relative to the begin of + // the epoch. + // This is not the correct value of the timestamp, but good enough for + // comparison. + return Date.parse("T" + String.trim(value.textContent)); + }); + + let minTimestamp = Math.min.apply(null, aTimestampMilliseconds); + let maxTimestamp = Math.max.apply(null, aTimestampMilliseconds); + ok(Math.abs(maxTimestamp - minTimestamp) < 2000, + "console.log message timestamp spread < 2000ms confirmed"); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js b/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js new file mode 100644 index 000000000..ace13f8d1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js @@ -0,0 +1,55 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* We are loading: +a script that is allowed by the CSP header but not by the CSPRO header +an image which is allowed by the CSPRO header but not by the CSP header. + +So we expect a warning (image has been blocked) and a report + (script should not load and was reported) + +The expected console messages in the constants CSP_VIOLATION_MSG and +CSP_REPORT_MSG are confirmed to be found in the console messages. +*/ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Web Console CSP report only " + + "test (bug 1010953)"; +const TEST_VIOLATION = "http://example.com/browser/devtools/client/" + + "webconsole/test/test_bug_1010953_cspro.html"; +const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " + + "blocked the loading of a resource at " + + "http://some.example.com/test.png " + + "(\u201cimg-src http://example.com\u201d)."; +const CSP_REPORT_MSG = "Content Security Policy: The page\u2019s settings " + + "observed the loading of a resource at " + + "http://some.example.com/test_bug_1010953_cspro.js " + + "(\u201cscript-src http://example.com\u201d). A CSP report is " + + "being sent."; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_VIOLATION); + yield loaded; + + yield waitForSuccess({ + name: "Confirmed that CSP and CSP-Report-Only log different messages to " + + "the console.", + validator: function () { + console.log(hud.outputNode.textContent); + let success = false; + success = hud.outputNode.textContent.indexOf(CSP_VIOLATION_MSG) > -1 && + hud.outputNode.textContent.indexOf(CSP_REPORT_MSG) > -1; + return success; + } + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js b/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js new file mode 100644 index 000000000..9b220b4a2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js @@ -0,0 +1,60 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that clicking on a function displays its source in the debugger. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug_1050691_click_function_to_source.html"; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + // Open the Debugger panel. + let debuggerPanel = yield openDebugger(); + // And right after come back to the Console panel. + yield openConsole(); + yield testWithDebuggerOpen(hud, debuggerPanel); +}); + +function* testWithDebuggerOpen(hud, debuggerPanel) { + let clickable = yield printFunction(hud); + let panelWin = debuggerPanel.panelWin; + let onEditorLocationSet = panelWin.once(panelWin.EVENTS.EDITOR_LOCATION_SET); + synthesizeClick(clickable, hud); + yield onEditorLocationSet; + ok(isDebuggerCaretPos(debuggerPanel, 7), + "Clicking on a function should go to its source in the debugger view"); +} + +function synthesizeClick(clickable, hud) { + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); +} + +var printFunction = Task.async(function* (hud) { + hud.jsterm.clearOutput(); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.foo(); + }); + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + let msg = [...result.matched][0]; + let clickable = msg.querySelector("a"); + ok(clickable, "clickable item for object should exist"); + return clickable; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js b/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js new file mode 100644 index 000000000..26bac7f57 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js @@ -0,0 +1,40 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console CSP messages for two META policies +// are correctly displayed. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test"; +const TEST_VIOLATION = "https://example.com/browser/devtools/client/" + + "webconsole/test/test_bug_1247459_violation.html"; +const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " + + "blocked the loading of a resource at " + + "http://some.example.com/test.png (\u201cimg-src " + + "https://example.com\u201d)."; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_VIOLATION); + yield loaded; + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "CSP policy URI warning displayed successfully", + text: CSP_VIOLATION_MSG, + repeats: 2 + } + ] + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js b/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js new file mode 100644 index 000000000..fb0182d8b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js @@ -0,0 +1,41 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the console object still exists after a page reload. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +var browser; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then((tab) => { + browser = tab.browser; + + browser.addEventListener("DOMContentLoaded", testPageReload, false); + content.location.reload(); + }); + }); + browser.addEventListener("DOMContentLoaded", onLoad, false); +} + +function testPageReload() { + browser.removeEventListener("DOMContentLoaded", testPageReload, false); + + let console = browser.contentWindow.wrappedJSObject.console; + + is(typeof console, "object", "window.console is an object, after page reload"); + is(typeof console.log, "function", "console.log is a function"); + is(typeof console.info, "function", "console.info is a function"); + is(typeof console.warn, "function", "console.warn is a function"); + is(typeof console.error, "function", "console.error is a function"); + is(typeof console.exception, "function", "console.exception is a function"); + + browser = null; + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js b/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js new file mode 100644 index 000000000..551dbd361 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js @@ -0,0 +1,20 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the input field is focused when the console is opened. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let inputNode = hud.jsterm.inputNode; + ok(inputNode.getAttribute("focused"), "input node is focused"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js b/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js new file mode 100644 index 000000000..4c5fbf9c8 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests to ensure that errors don't appear when the console is closed while a +// completion is being performed. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + yield testClosingAfterCompletion(hud, browser); +}); + +function testClosingAfterCompletion(hud, browser) { + let deferred = promise.defer(); + + let errorWhileClosing = false; + function errorListener() { + errorWhileClosing = true; + } + + browser.addEventListener("error", errorListener, false); + + // Focus the jsterm and perform the keycombo to close the WebConsole. + hud.jsterm.focus(); + + gDevTools.once("toolbox-destroyed", function () { + browser.removeEventListener("error", errorListener, false); + is(errorWhileClosing, false, "no error while closing the WebConsole"); + deferred.resolve(); + }); + + if (Services.appinfo.OS == "Darwin") { + EventUtils.synthesizeKey("i", { accelKey: true, altKey: true }); + } else { + EventUtils.synthesizeKey("i", { accelKey: true, shiftKey: true }); + } + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js b/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js new file mode 100644 index 000000000..af00bf913 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that errors still show up in the Web Console after a page reload. +// See bug 580030: the error handler fails silently after page reload. +// https://bugzilla.mozilla.org/show_bug.cgi?id=580030 + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-error.html"; + +function test() { + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + info("console opened"); + + executeSoon(() => { + hud.jsterm.clearOutput(); + info("wait for reload"); + content.location.reload(); + }); + + yield hud.target.once("navigate"); + info("target navigated"); + + let button = content.document.querySelector("button"); + ok(button, "button found"); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + EventUtils.sendMouseEvent({type: "click"}, button, content); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "fooBazBaz is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js b/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js new file mode 100644 index 000000000..6cd03164b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that appropriately-localized timestamps are printed. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + const TEST_TIMESTAMP = 12345678; + let date = new Date(TEST_TIMESTAMP); + let localizedString = WCUL10n.timestampString(TEST_TIMESTAMP); + isnot(localizedString.indexOf(date.getHours()), -1, "the localized " + + "timestamp contains the hours"); + isnot(localizedString.indexOf(date.getMinutes()), -1, "the localized " + + "timestamp contains the minutes"); + isnot(localizedString.indexOf(date.getSeconds()), -1, "the localized " + + "timestamp contains the seconds"); + isnot(localizedString.indexOf(date.getMilliseconds()), -1, "the localized " + + "timestamp contains the milliseconds"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js b/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js new file mode 100644 index 000000000..5e7b141eb --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js @@ -0,0 +1,49 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that exceptions thrown by content don't show up twice in the Web +// Console. + +"use strict"; + +const INIT_URI = "data:text/html;charset=utf8,hello world"; +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-duplicate-error.html"; + +add_task(function* () { + yield loadTab(INIT_URI); + + let hud = yield openConsole(); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "fooDuplicateError1", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "test-duplicate-error.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); + + let text = hud.outputNode.textContent; + let error1pos = text.indexOf("fooDuplicateError1"); + ok(error1pos > -1, "found fooDuplicateError1"); + if (error1pos > -1) { + ok(text.indexOf("fooDuplicateError1", error1pos + 1) == -1, + "no duplicate for fooDuplicateError1"); + } +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js b/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js new file mode 100644 index 000000000..7dd271388 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/browser/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + testCompletion(hud); +}); + +function testCompletion(hud) { + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + + jsterm.setInputValue(""); + EventUtils.synthesizeKey("VK_TAB", {}); + is(jsterm.completeNode.value, "<- no result", "<- no result - matched"); + is(input.value, "", "inputnode is empty - matched"); + is(input.getAttribute("focused"), "true", "input is still focused"); + + // Any thing which is not in property autocompleter + jsterm.setInputValue("window.Bug583816"); + EventUtils.synthesizeKey("VK_TAB", {}); + is(jsterm.completeNode.value, " <- no result", + "completenode content - matched"); + is(input.value, "window.Bug583816", "inputnode content - matched"); + is(input.getAttribute("focused"), "true", "input is still focused"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js b/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js new file mode 100644 index 000000000..974557ec0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js @@ -0,0 +1,89 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console limits the number of lines displayed according to +// the user's preferences. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,test for bug 585237"; + +var outputNode; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + outputNode = hud.outputNode; + + hud.jsterm.clearOutput(); + + let prefBranch = Services.prefs.getBranch("devtools.hud.loglimit."); + prefBranch.setIntPref("console", 20); + + for (let i = 0; i < 30; i++) { + yield ContentTask.spawn(gBrowser.selectedBrowser, i, function (i) { + // must change message to prevent repeats + content.console.log("foo #" + i); + }); + } + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo #29", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(countMessageNodes(), 20, "there are 20 message nodes in the output " + + "when the log limit is set to 20"); + + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () { + content.console.log("bar bug585237"); + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bar bug585237", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(countMessageNodes(), 20, "there are still 20 message nodes in the " + + "output when adding one more"); + + prefBranch.setIntPref("console", 30); + for (let i = 0; i < 20; i++) { + yield ContentTask.spawn(gBrowser.selectedBrowser, i, function (i) { + // must change message to prevent repeats + content.console.log("boo #" + i); + }); + } + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "boo #19", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(countMessageNodes(), 30, "there are 30 message nodes in the output " + + "when the log limit is set to 30"); + + prefBranch.clearUserPref("console"); + + outputNode = null; +}); + +function countMessageNodes() { + return outputNode.querySelectorAll(".message").length; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js b/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js new file mode 100644 index 000000000..c38fd52c1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js @@ -0,0 +1,70 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-bug-585956-console-trace.html"; + +add_task(function* () { + let {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello"); + let hud = yield openConsole(tab); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.trace output", + consoleTrace: { + file: "test-bug-585956-console-trace.html", + fn: "window.foobar585956c", + }, + }], + }); + + let node = [...result.matched][0]; + ok(node, "found trace log node"); + + let obj = node._messageObject; + ok(obj, "console.trace message object"); + + // The expected stack trace object. + let stacktrace = [ + { + columnNumber: 3, + filename: TEST_URI, + functionName: "window.foobar585956c", + language: 2, + lineNumber: 9 + }, + { + columnNumber: 10, + filename: TEST_URI, + functionName: "foobar585956b", + language: 2, + lineNumber: 14 + }, + { + columnNumber: 10, + filename: TEST_URI, + functionName: "foobar585956a", + language: 2, + lineNumber: 18 + }, + { + columnNumber: 1, + filename: TEST_URI, + functionName: "", + language: 2, + lineNumber: 21 + } + ]; + + ok(obj._stacktrace, "found stacktrace object"); + is(obj._stacktrace.toSource(), stacktrace.toSource(), + "stacktrace is correct"); + isnot(node.textContent.indexOf("bug-585956"), -1, "found file name"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js new file mode 100644 index 000000000..0021a8cc1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js @@ -0,0 +1,367 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " + + "popup keyboard usage test"; + +// We should turn off auto-multiline editing during these tests +const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline"; +var HUD, popup, jsterm, inputNode, completeNode; + +add_task(function* () { + Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false); + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + yield consoleOpened(hud); + yield popupHideAfterTab(); + yield testReturnKey(); + yield dontShowArrayNumbers(); + yield testReturnWithNoSelection(); + yield popupHideAfterReturnWithNoSelection(); + yield testCompletionInText(); + yield popupHideAfterCompletionInText(); + + HUD = popup = jsterm = inputNode = completeNode = null; + Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true); +}); + +var consoleOpened = Task.async(function* (hud) { + let deferred = promise.defer(); + HUD = hud; + info("web console opened"); + + jsterm = HUD.jsterm; + + yield jsterm.execute("window.foobarBug585991={" + + "'item0': 'value0'," + + "'item1': 'value1'," + + "'item2': 'value2'," + + "'item3': 'value3'" + + "}"); + yield jsterm.execute("window.testBug873250a = 'hello world';" + + "window.testBug873250b = 'hello world 2';"); + popup = jsterm.autocompletePopup; + completeNode = jsterm.completeNode; + inputNode = jsterm.inputNode; + + ok(!popup.isOpen, "popup is not open"); + + popup.once("popup-opened", () => { + ok(popup.isOpen, "popup is open"); + + // 4 values, and the following properties: + // __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ + // __proto__ hasOwnProperty isPrototypeOf propertyIsEnumerable + // toLocaleString toString toSource unwatch valueOf watch constructor. + is(popup.itemCount, 19, "popup.itemCount is correct"); + + let sameItems = popup.getItems().reverse().map(function (e) { + return e.label; + }); + + ok(sameItems.every(function (prop, index) { + return [ + "__defineGetter__", + "__defineSetter__", + "__lookupGetter__", + "__lookupSetter__", + "__proto__", + "constructor", + "hasOwnProperty", + "isPrototypeOf", + "item0", + "item1", + "item2", + "item3", + "propertyIsEnumerable", + "toLocaleString", + "toSource", + "toString", + "unwatch", + "valueOf", + "watch", + ][index] === prop; + }), "getItems returns the items we expect"); + + is(popup.selectedIndex, 18, + "Index of the first item from bottom is selected."); + EventUtils.synthesizeKey("VK_DOWN", {}); + + let prefix = jsterm.getInputValue().replace(/[\S]/g, " "); + + is(popup.selectedIndex, 0, "index 0 is selected"); + is(popup.selectedItem.label, "watch", "watch is selected"); + is(completeNode.value, prefix + "watch", + "completeNode.value holds watch"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(popup.selectedIndex, 1, "index 1 is selected"); + is(popup.selectedItem.label, "valueOf", "valueOf is selected"); + is(completeNode.value, prefix + "valueOf", + "completeNode.value holds valueOf"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(popup.selectedIndex, 0, "index 0 is selected"); + is(popup.selectedItem.label, "watch", "watch is selected"); + is(completeNode.value, prefix + "watch", + "completeNode.value holds watch"); + + let currentSelectionIndex = popup.selectedIndex; + + EventUtils.synthesizeKey("VK_PAGE_DOWN", {}); + + ok(popup.selectedIndex > currentSelectionIndex, + "Index is greater after PGDN"); + + currentSelectionIndex = popup.selectedIndex; + EventUtils.synthesizeKey("VK_PAGE_UP", {}); + + ok(popup.selectedIndex < currentSelectionIndex, + "Index is less after Page UP"); + + EventUtils.synthesizeKey("VK_END", {}); + is(popup.selectedIndex, 18, "index is last after End"); + + EventUtils.synthesizeKey("VK_HOME", {}); + is(popup.selectedIndex, 0, "index is first after Home"); + + info("press Tab and wait for popup to hide"); + popup.once("popup-closed", () => { + deferred.resolve(); + }); + EventUtils.synthesizeKey("VK_TAB", {}); + }); + + jsterm.setInputValue("window.foobarBug585991"); + EventUtils.synthesizeKey(".", {}); + + return deferred.promise; +}); + +function popupHideAfterTab() { + let deferred = promise.defer(); + + // At this point the completion suggestion should be accepted. + ok(!popup.isOpen, "popup is not open"); + + is(jsterm.getInputValue(), "window.foobarBug585991.watch", + "completion was successful after VK_TAB"); + + ok(!completeNode.value, "completeNode is empty"); + + popup.once("popup-opened", function onShown() { + ok(popup.isOpen, "popup is open"); + + is(popup.itemCount, 19, "popup.itemCount is correct"); + + is(popup.selectedIndex, 18, "First index from bottom is selected"); + EventUtils.synthesizeKey("VK_DOWN", {}); + + let prefix = jsterm.getInputValue().replace(/[\S]/g, " "); + + is(popup.selectedIndex, 0, "index 0 is selected"); + is(popup.selectedItem.label, "watch", "watch is selected"); + is(completeNode.value, prefix + "watch", + "completeNode.value holds watch"); + + popup.once("popup-closed", function onHidden() { + ok(!popup.isOpen, "popup is not open after VK_ESCAPE"); + + is(jsterm.getInputValue(), "window.foobarBug585991.", + "completion was cancelled"); + + ok(!completeNode.value, "completeNode is empty"); + + deferred.resolve(); + }, false); + + info("press Escape to close the popup"); + executeSoon(function () { + EventUtils.synthesizeKey("VK_ESCAPE", {}); + }); + }, false); + + info("wait for completion: window.foobarBug585991."); + executeSoon(function () { + jsterm.setInputValue("window.foobarBug585991"); + EventUtils.synthesizeKey(".", {}); + }); + + return deferred.promise; +} + +function testReturnKey() { + let deferred = promise.defer(); + + popup.once("popup-opened", function onShown() { + ok(popup.isOpen, "popup is open"); + + is(popup.itemCount, 19, "popup.itemCount is correct"); + + is(popup.selectedIndex, 18, "First index from bottom is selected"); + EventUtils.synthesizeKey("VK_DOWN", {}); + + let prefix = jsterm.getInputValue().replace(/[\S]/g, " "); + + is(popup.selectedIndex, 0, "index 0 is selected"); + is(popup.selectedItem.label, "watch", "watch is selected"); + is(completeNode.value, prefix + "watch", + "completeNode.value holds watch"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(popup.selectedIndex, 1, "index 1 is selected"); + is(popup.selectedItem.label, "valueOf", "valueOf is selected"); + is(completeNode.value, prefix + "valueOf", + "completeNode.value holds valueOf"); + + popup.once("popup-closed", function onHidden() { + ok(!popup.isOpen, "popup is not open after VK_RETURN"); + + is(jsterm.getInputValue(), "window.foobarBug585991.valueOf", + "completion was successful after VK_RETURN"); + + ok(!completeNode.value, "completeNode is empty"); + + deferred.resolve(); + }, false); + + info("press Return to accept suggestion. wait for popup to hide"); + + executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {})); + }, false); + + info("wait for completion suggestions: window.foobarBug585991."); + + executeSoon(function () { + jsterm.setInputValue("window.foobarBug58599"); + EventUtils.synthesizeKey("1", {}); + EventUtils.synthesizeKey(".", {}); + }); + + return deferred.promise; +} + +function* dontShowArrayNumbers() { + let deferred = promise.defer(); + + info("dontShowArrayNumbers"); + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.foobarBug585991 = ["Sherlock Holmes"]; + }); + + jsterm = HUD.jsterm; + popup = jsterm.autocompletePopup; + + popup.once("popup-opened", function onShown() { + let sameItems = popup.getItems().map(function (e) { + return e.label; + }); + ok(!sameItems.some(function (prop) { + prop === "0"; + }), "Completing on an array doesn't show numbers."); + + popup.once("popup-closed", function popupHidden() { + deferred.resolve(); + }, false); + + info("wait for popup to hide"); + executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {})); + }, false); + + info("wait for popup to show"); + executeSoon(() => { + jsterm.setInputValue("window.foobarBug585991"); + EventUtils.synthesizeKey(".", {}); + }); + + return deferred.promise; +} + +function testReturnWithNoSelection() { + let deferred = promise.defer(); + + info("test pressing return with open popup, but no selection, see bug 873250"); + + popup.once("popup-opened", function onShown() { + ok(popup.isOpen, "popup is open"); + is(popup.itemCount, 2, "popup.itemCount is correct"); + isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct"); + + info("press Return and wait for popup to hide"); + popup.once("popup-closed", function popupHidden() { + deferred.resolve(); + }); + executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {})); + }); + + executeSoon(() => { + info("wait for popup to show"); + jsterm.setInputValue("window.testBu"); + EventUtils.synthesizeKey("g", {}); + }); + + return deferred.promise; +} + +function popupHideAfterReturnWithNoSelection() { + ok(!popup.isOpen, "popup is not open after VK_RETURN"); + + is(jsterm.getInputValue(), "", "inputNode is empty after VK_RETURN"); + is(completeNode.value, "", "completeNode is empty"); + is(jsterm.history[jsterm.history.length - 1], "window.testBug", + "jsterm history is correct"); + + return promise.resolve(); +} + +function testCompletionInText() { + info("test that completion works inside text, see bug 812618"); + + let deferred = promise.defer(); + + popup.once("popup-opened", function onShown() { + ok(popup.isOpen, "popup is open"); + is(popup.itemCount, 2, "popup.itemCount is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + is(popup.selectedIndex, 0, "popup.selectedIndex is correct"); + ok(!completeNode.value, "completeNode.value is empty"); + + let items = popup.getItems().reverse().map(e => e.label); + let sameItems = items.every((prop, index) => + ["testBug873250a", "testBug873250b"][index] === prop); + ok(sameItems, "getItems returns the items we expect"); + + info("press Tab and wait for popup to hide"); + popup.once("popup-closed", function popupHidden() { + deferred.resolve(); + }); + EventUtils.synthesizeKey("VK_TAB", {}); + }); + + jsterm.setInputValue("dump(window.testBu)"); + inputNode.selectionStart = inputNode.selectionEnd = 18; + EventUtils.synthesizeKey("g", {}); + return deferred.promise; +} + +function popupHideAfterCompletionInText() { + // At this point the completion suggestion should be accepted. + ok(!popup.isOpen, "popup is not open"); + is(jsterm.getInputValue(), "dump(window.testBug873250b)", + "completion was successful after VK_TAB"); + is(inputNode.selectionStart, 26, "cursor location is correct"); + is(inputNode.selectionStart, inputNode.selectionEnd, + "cursor location (confirmed)"); + ok(!completeNode.value, "completeNode is empty"); + + return promise.resolve(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js new file mode 100644 index 000000000..df1a42edf --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js @@ -0,0 +1,123 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " + + "popup test"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + yield consoleOpened(hud); +}); + +function consoleOpened(HUD) { + let deferred = promise.defer(); + + let items = [ + {label: "item0", value: "value0"}, + {label: "item1", value: "value1"}, + {label: "item2", value: "value2"}, + ]; + + let popup = HUD.jsterm.autocompletePopup; + let input = HUD.jsterm.inputNode; + + ok(!popup.isOpen, "popup is not open"); + ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant"); + + popup.once("popup-opened", () => { + ok(popup.isOpen, "popup is open"); + + is(popup.itemCount, 0, "no items"); + ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant"); + + popup.setItems(items); + + is(popup.itemCount, items.length, "items added"); + + let sameItems = popup.getItems(); + is(sameItems.every(function (item, index) { + return item === items[index]; + }), true, "getItems returns back the same items"); + + is(popup.selectedIndex, 2, "Index of the first item from bottom is selected."); + is(popup.selectedItem, items[2], "First item from bottom is selected"); + checkActiveDescendant(popup, input); + + popup.selectedIndex = 1; + + is(popup.selectedIndex, 1, "index 1 is selected"); + is(popup.selectedItem, items[1], "item1 is selected"); + checkActiveDescendant(popup, input); + + popup.selectedItem = items[2]; + + is(popup.selectedIndex, 2, "index 2 is selected"); + is(popup.selectedItem, items[2], "item2 is selected"); + checkActiveDescendant(popup, input); + + is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works"); + + is(popup.selectedIndex, 1, "index 1 is selected"); + is(popup.selectedItem, items[1], "item1 is selected"); + checkActiveDescendant(popup, input); + + is(popup.selectNextItem(), items[2], "selectNextItem() works"); + + is(popup.selectedIndex, 2, "index 2 is selected"); + is(popup.selectedItem, items[2], "item2 is selected"); + checkActiveDescendant(popup, input); + + ok(popup.selectNextItem(), "selectNextItem() works"); + + is(popup.selectedIndex, 0, "index 0 is selected"); + is(popup.selectedItem, items[0], "item0 is selected"); + checkActiveDescendant(popup, input); + + items.push({label: "label3", value: "value3"}); + popup.appendItem(items[3]); + + is(popup.itemCount, items.length, "item3 appended"); + + popup.selectedIndex = 3; + is(popup.selectedItem, items[3], "item3 is selected"); + checkActiveDescendant(popup, input); + + popup.removeItem(items[2]); + + is(popup.selectedIndex, 2, "index2 is selected"); + is(popup.selectedItem, items[3], "item3 is still selected"); + checkActiveDescendant(popup, input); + is(popup.itemCount, items.length - 1, "item2 removed"); + + popup.clearItems(); + is(popup.itemCount, 0, "items cleared"); + ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant"); + + popup.once("popup-closed", () => { + deferred.resolve(); + }); + popup.hidePopup(); + }); + + popup.openPopup(input); + + return deferred.promise; +} + +function checkActiveDescendant(popup, input) { + let activeElement = input.ownerDocument.activeElement; + let descendantId = activeElement.getAttribute("aria-activedescendant"); + let popupItem = popup._tooltip.panel.querySelector("#" + descendantId); + let cloneItem = input.ownerDocument.querySelector("#" + descendantId); + + ok(popupItem, "Active descendant is found in the popup list"); + ok(cloneItem, "Active descendant is found in the list clone"); + is(popupItem.innerHTML, cloneItem.innerHTML, + "Cloned item has the same HTML as the original element"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js b/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js new file mode 100644 index 000000000..cda31191e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js @@ -0,0 +1,84 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + yield testSelectionWhenMovingBetweenBoxes(hud); + performTestsAfterOutput(hud); +}); + +var testSelectionWhenMovingBetweenBoxes = Task.async(function* (hud) { + let jsterm = hud.jsterm; + + // Fill the console with some output. + jsterm.clearOutput(); + yield jsterm.execute("1 + 2"); + yield jsterm.execute("3 + 4"); + yield jsterm.execute("5 + 6"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "3", + category: CATEGORY_OUTPUT, + }, + { + text: "7", + category: CATEGORY_OUTPUT, + }, + { + text: "11", + category: CATEGORY_OUTPUT, + }], + }); +}); + +function performTestsAfterOutput(hud) { + let outputNode = hud.outputNode; + + ok(outputNode.childNodes.length >= 3, "the output node has children after " + + "executing some JavaScript"); + + // Test that the global Firefox "Select All" functionality (e.g. Edit > + // Select All) works properly in the Web Console. + let commandController = hud.ui._commandController; + ok(commandController != null, "the window has a command controller object"); + + commandController.selectAll(); + + let selectedCount = hud.ui.output.getSelectedMessages().length; + is(selectedCount, outputNode.childNodes.length, + "all console messages are selected after performing a regular browser " + + "select-all operation"); + + hud.iframeWindow.getSelection().removeAllRanges(); + + // Test the context menu "Select All" (which has a different code path) works + // properly as well. + let contextMenuId = hud.ui.outputWrapper.getAttribute("context"); + let contextMenu = hud.ui.document.getElementById(contextMenuId); + ok(contextMenu != null, "the output node has a context menu"); + + let selectAllItem = contextMenu.querySelector("*[command='cmd_selectAll']"); + ok(selectAllItem != null, + "the context menu on the output node has a \"Select All\" item"); + + outputNode.focus(); + + selectAllItem.doCommand(); + + selectedCount = hud.ui.output.getSelectedMessages().length; + is(selectedCount, outputNode.childNodes.length, + "all console messages are selected after performing a select-all " + + "operation from the context menu"); + + hud.iframeWindow.getSelection().removeAllRanges(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js new file mode 100644 index 000000000..208baf3d6 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js @@ -0,0 +1,106 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ +/* globals goUpdateCommand goDoCommand */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +var HUD, outputNode; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + yield consoleOpened(hud); + yield testContextMenuCopy(); + + HUD = outputNode = null; +}); + +function consoleOpened(hud) { + HUD = hud; + + let deferred = promise.defer(); + + // See bugs 574036, 586386 and 587617. + outputNode = HUD.outputNode; + + HUD.jsterm.clearOutput(); + + let controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled"); + + ContentTask.spawn(gBrowser.selectedBrowser, null, + "() => content.console.log('Hello world! bug587617')"); + + waitForMessages({ + webconsole: HUD, + messages: [{ + text: "bug587617", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(([result]) => { + let msg = [...result.matched][0]; + HUD.ui.output.selectMessage(msg); + + outputNode.focus(); + + goUpdateCommand("cmd_copy"); + controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); + + // Remove new lines and whitespace since getSelection() includes + // a new line between message and line number, but the clipboard doesn't + // @see bug 1119503 + let selection = (HUD.iframeWindow.getSelection() + "") + .replace(/\r?\n|\r| /g, ""); + isnot(selection.indexOf("bug587617"), -1, + "selection text includes 'bug587617'"); + + waitForClipboard((str) => { + // Strip out spaces for comparison ease + return selection.trim() == str.trim().replace(/ /g, ""); + }, () => { + goDoCommand("cmd_copy"); + }, deferred.resolve, deferred.resolve); + }); + return deferred.promise; +} + +// Test that the context menu "Copy" (which has a different code path) works +// properly as well. +function testContextMenuCopy() { + let deferred = promise.defer(); + + let contextMenuId = HUD.ui.outputWrapper.getAttribute("context"); + let contextMenu = HUD.ui.document.getElementById(contextMenuId); + ok(contextMenu, "the output node has a context menu"); + + let copyItem = contextMenu.querySelector("*[command='cmd_copy']"); + ok(copyItem, "the context menu on the output node has a \"Copy\" item"); + + // Remove new lines and whitespace since getSelection() includes + // a new line between message and line number, but the clipboard doesn't + // @see bug 1119503 + let selection = (HUD.iframeWindow.getSelection() + "") + .replace(/\r?\n|\r| /g, ""); + + copyItem.doCommand(); + + waitForClipboard((str) => { + // Strip out spaces for comparison ease + return selection.trim() == str.trim().replace(/ /g, ""); + }, () => { + goDoCommand("cmd_copy"); + }, deferred.resolve, deferred.resolve); + HUD = outputNode = null; + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js b/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js new file mode 100644 index 000000000..ff926fc13 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 588342"; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + let hud = yield openConsole(); + + yield checkConsoleFocus(hud); + + let isFocused = yield ContentTask.spawn(browser, { }, function* () { + var fm = Components.classes["@mozilla.org/focus-manager;1"]. + getService(Components.interfaces.nsIFocusManager); + return fm.focusedWindow == content; + }); + + ok(isFocused, "content document has focus"); +}); + +function* checkConsoleFocus(hud) { + let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); + + yield new Promise(resolve => { + waitForFocus(resolve); + }); + + is(hud.jsterm.inputNode.getAttribute("focused"), "true", + "jsterm input is focused on web console open"); + is(fm.focusedWindow, hud.iframeWindow, "hud window is focused"); + yield closeConsole(null); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js b/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js new file mode 100644 index 000000000..94a0ad77e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js @@ -0,0 +1,53 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that adding text to one of the output labels doesn't cause errors. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield testTextNodeInsertion(hud); +}); + +// Test for bug 588730: Adding a text node to an existing label element causes +// warnings +function testTextNodeInsertion(hud) { + let deferred = promise.defer(); + let outputNode = hud.outputNode; + + let label = document.createElementNS( + "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label"); + outputNode.appendChild(label); + + let error = false; + let listener = { + observe: function (aMessage) { + let messageText = aMessage.message; + if (messageText.indexOf("JavaScript Warning") !== -1) { + error = true; + } + } + }; + + Services.console.registerListener(listener); + + // This shouldn't fail. + label.appendChild(document.createTextNode("foo")); + + executeSoon(function () { + Services.console.unregisterListener(listener); + ok(!error, "no error when adding text nodes as children of labels"); + + return deferred.resolve(); + }); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js b/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js new file mode 100644 index 000000000..c590495c4 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js @@ -0,0 +1,44 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + testInputExpansion(hud); +}); + +function testInputExpansion(hud) { + let input = hud.jsterm.inputNode; + + input.focus(); + + is(input.getAttribute("multiline"), "true", "multiline is enabled"); + + let ordinaryHeight = input.clientHeight; + + // Tests if the inputNode expands. + input.value = "hello\nworld\n"; + let length = input.value.length; + input.selectionEnd = length; + input.selectionStart = length; + // Performs an "d". This will trigger/test for the input event that should + // change the height of the inputNode. + EventUtils.synthesizeKey("d", {}); + ok(input.clientHeight > ordinaryHeight, "the input expanded"); + + // Test if the inputNode shrinks again. + input.value = ""; + EventUtils.synthesizeKey("d", {}); + is(input.clientHeight, ordinaryHeight, "the input's height is normal again"); + + input = length = null; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js b/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js new file mode 100644 index 000000000..509c875f8 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<div style='font-size:3em;" + + "foobarCssParser:baz'>test CSS parser filter</div>"; + +/** + * Unit test for bug 589162: + * CSS filtering on the console does not work + */ +add_task(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + + // CSS warnings are disabled by default. + hud.setFilterState("cssparser", true); + hud.jsterm.clearOutput(); + + BrowserReload(); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarCssParser", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }], + }); + + hud.setFilterState("cssparser", false); + + let msg = "the unknown CSS property warning is not displayed, " + + "after filtering"; + testLogEntry(hud.outputNode, "foobarCssParser", msg, true, true); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js b/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js new file mode 100644 index 000000000..adbf13086 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js @@ -0,0 +1,29 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that, when the user types an extraneous closing bracket, no error +// appears. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,test for bug 592442"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + let jsterm = hud.jsterm; + + jsterm.setInputValue("document.getElementById)"); + + let error = false; + try { + jsterm.complete(jsterm.COMPLETE_HINT_ONLY); + } catch (ex) { + error = true; + } + + ok(!error, "no error was thrown when an extraneous bracket was inserted"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js b/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js new file mode 100644 index 000000000..9f429a3d1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js @@ -0,0 +1,68 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-593003-iframe-wrong-hud.html"; + +const TEST_IFRAME_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-bug-593003-iframe-wrong-" + + "hud-iframe.html"; + +const TEST_DUMMY_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-console.html"; + +add_task(function* () { + + let tab1 = (yield loadTab(TEST_URI)).tab; + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.console.log("FOO"); + }); + yield openConsole(); + + let tab2 = (yield loadTab(TEST_DUMMY_URI)).tab; + yield openConsole(gBrowser.selectedTab); + + info("Reloading tab 1"); + yield reloadTab(tab1); + + info("Checking for messages"); + yield checkMessages(tab1, tab2); + + info("Cleaning up"); + yield closeConsole(tab1); + yield closeConsole(tab2); +}); + +function* reloadTab(tab) { + let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + tab.linkedBrowser.reload(); + yield loaded; +} + +function* checkMessages(tab1, tab2) { + let hud1 = yield openConsole(tab1); + let outputNode1 = hud1.outputNode; + + info("Waiting for messages"); + yield waitForMessages({ + webconsole: hud1, + messages: [{ + text: TEST_IFRAME_URI, + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }] + }); + + let hud2 = yield openConsole(tab2); + let outputNode2 = hud2.outputNode; + + isnot(outputNode1, outputNode2, + "the two HUD outputNodes must be different"); + + let msg = "Didn't find the iframe network request in tab2"; + testLogEntry(outputNode2, TEST_IFRAME_URI, msg, true, true); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js b/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js new file mode 100644 index 000000000..514f875c0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js @@ -0,0 +1,155 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var jsterm, inputNode, values; + +var TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 594497 and bug 619598"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + setup(hud); + performTests(); + + jsterm = inputNode = values = null; +}); + +function setup(HUD) { + jsterm = HUD.jsterm; + inputNode = jsterm.inputNode; + + jsterm.focus(); + + ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty"); + + values = ["document", "window", "document.body"]; + values.push(values.join(";\n"), "document.location"); + + // Execute each of the values; + for (let i = 0; i < values.length; i++) { + jsterm.setInputValue(values[i]); + jsterm.execute(); + } +} + +function performTests() { + EventUtils.synthesizeKey("VK_UP", {}); + + + is(jsterm.getInputValue(), values[4], + "VK_UP: jsterm.getInputValue() #4 is correct"); + + ok(inputNode.selectionStart == values[4].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[3], + "VK_UP: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart == values[3].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + inputNode.setSelectionRange(values[3].length - 2, values[3].length - 2); + + EventUtils.synthesizeKey("VK_UP", {}); + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[3], + "VK_UP two times: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart == jsterm.getInputValue().indexOf("\n") && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[3], + "VK_UP again: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart == 0 && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[2], + "VK_UP: jsterm.getInputValue() #2 is correct"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[1], + "VK_UP: jsterm.getInputValue() #1 is correct"); + + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.getInputValue(), values[0], + "VK_UP: jsterm.getInputValue() #0 is correct"); + + ok(inputNode.selectionStart == values[0].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[1], + "VK_DOWN: jsterm.getInputValue() #1 is correct"); + + ok(inputNode.selectionStart == values[1].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[2], + "VK_DOWN: jsterm.getInputValue() #2 is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[3], + "VK_DOWN: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart == values[3].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + inputNode.setSelectionRange(2, 2); + + EventUtils.synthesizeKey("VK_DOWN", {}); + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[3], + "VK_DOWN two times: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart > jsterm.getInputValue().lastIndexOf("\n") && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[3], + "VK_DOWN again: jsterm.getInputValue() #3 is correct"); + + ok(inputNode.selectionStart == values[3].length && + inputNode.selectionStart == inputNode.selectionEnd, + "caret location is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + is(jsterm.getInputValue(), values[4], + "VK_DOWN: jsterm.getInputValue() #4 is correct"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + + ok(!jsterm.getInputValue(), + "VK_DOWN: jsterm.getInputValue() is empty"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js new file mode 100644 index 000000000..d57d724ca --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const PREF = "devtools.webconsole.persistlog"; +const TEST_FILE = "test-network.html"; +const TEST_URI = "data:text/html;charset=utf8,<p>test file URI"; + +var hud; + +add_task(function* () { + Services.prefs.setBoolPref(PREF, true); + + let jar = getJar(getRootDirectory(gTestPath)); + let dir = jar ? + extractJarToTmp(jar) : + getChromeDir(getResolvedURI(gTestPath)); + + dir.append(TEST_FILE); + let uri = Services.io.newFileURI(dir); + + let { browser } = yield loadTab(TEST_URI); + + hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri.spec); + yield loaded; + + yield testMessages(); + + Services.prefs.clearUserPref(PREF); + hud = null; +}); + +function testMessages() { + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "running network console logging tests", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "test-network.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "test-image.png", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "testscript.js", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js b/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js new file mode 100644 index 000000000..1951cb366 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js @@ -0,0 +1,100 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console doesn't leak when multiple tabs and windows are +// opened and then closed. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 595350"; + +var win1 = window, win2; +var openTabs = []; +var loadedTabCount = 0; + +function test() { + requestLongerTimeout(3); + + // Add two tabs in the main window. + addTabs(win1); + + // Open a new window. + win2 = OpenBrowserWindow(); + win2.addEventListener("load", onWindowLoad, true); +} + +function onWindowLoad(aEvent) { + win2.removeEventListener(aEvent.type, onWindowLoad, true); + + // Add two tabs in the new window. + addTabs(win2); +} + +function addTabs(aWindow) { + for (let i = 0; i < 2; i++) { + let tab = aWindow.gBrowser.addTab(TEST_URI); + openTabs.push(tab); + + tab.linkedBrowser.addEventListener("load", function onLoad(aEvent) { + tab.linkedBrowser.removeEventListener(aEvent.type, onLoad, true); + + loadedTabCount++; + info("tabs loaded: " + loadedTabCount); + if (loadedTabCount >= 4) { + executeSoon(openConsoles); + } + }, true); + } +} + +function openConsoles() { + function open(i) { + let tab = openTabs[i]; + openConsole(tab).then(function (hud) { + ok(hud, "HUD is open for tab " + i); + let window = hud.target.tab.linkedBrowser.contentWindow; + window.console.log("message for tab " + i); + + if (i >= openTabs.length - 1) { + // Use executeSoon() to allow the promise to resolve. + executeSoon(closeConsoles); + } + else { + executeSoon(() => open(i + 1)); + } + }); + } + + // open the Web Console for each of the four tabs and log a message. + open(0); +} + +function closeConsoles() { + let consolesClosed = 0; + + function onWebConsoleClose(aSubject, aTopic) { + if (aTopic == "web-console-destroyed") { + consolesClosed++; + info("consoles destroyed: " + consolesClosed); + if (consolesClosed == 4) { + // Use executeSoon() to allow all the observers to execute. + executeSoon(finishTest); + } + } + } + + Services.obs.addObserver(onWebConsoleClose, "web-console-destroyed", false); + + registerCleanupFunction(() => { + Services.obs.removeObserver(onWebConsoleClose, "web-console-destroyed"); + }); + + win2.close(); + + win1.gBrowser.removeTab(openTabs[0]); + win1.gBrowser.removeTab(openTabs[1]); + + openTabs = win1 = win2 = null; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js b/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js new file mode 100644 index 000000000..855cfbb88 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js @@ -0,0 +1,211 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 595934 - message categories coverage."; +const TESTS_PATH = "http://example.com/browser/devtools/client/webconsole/" + + "test/"; +const TESTS = [ + { + // #0 + file: "test-bug-595934-css-loader.html", + category: "CSS Loader", + matchString: "text/css", + }, + { + // #1 + file: "test-bug-595934-imagemap.html", + category: "Layout: ImageMap", + matchString: "shape=\"rect\"", + }, + { + // #2 + file: "test-bug-595934-html.html", + category: "HTML", + matchString: "multipart/form-data", + onload: function () { + let form = content.document.querySelector("form"); + form.submit(); + }, + }, + { + // #3 + file: "test-bug-595934-workers.html", + category: "Web Worker", + matchString: "fooBarWorker", + }, + { + // #4 + file: "test-bug-595934-malformedxml.xhtml", + category: "malformed-xml", + matchString: "no root element found", + }, + { + // #5 + file: "test-bug-595934-svg.xhtml", + category: "SVG", + matchString: "fooBarSVG", + }, + { + // #6 + file: "test-bug-595934-css-parser.html", + category: "CSS Parser", + matchString: "foobarCssParser", + }, + { + // #7 + file: "test-bug-595934-malformedxml-external.html", + category: "malformed-xml", + matchString: "</html>", + }, + { + // #8 + file: "test-bug-595934-empty-getelementbyid.html", + category: "DOM", + matchString: "getElementById", + }, + { + // #9 + file: "test-bug-595934-canvas-css.html", + category: "CSS Parser", + matchString: "foobarCanvasCssParser", + }, + { + // #10 + file: "test-bug-595934-image.html", + category: "Image", + matchString: "corrupt", + }, +]; + +var pos = -1; + +var foundCategory = false; +var foundText = false; +var pageLoaded = false; +var pageError = false; +var output = null; +var jsterm = null; +var hud = null; +var testEnded = false; + +var TestObserver = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), + + observe: function testObserve(subject) { + if (testEnded || !(subject instanceof Ci.nsIScriptError)) { + return; + } + + let expectedCategory = TESTS[pos].category; + + info("test #" + pos + " console observer got " + subject.category + + ", is expecting " + expectedCategory); + + if (subject.category == expectedCategory) { + foundCategory = true; + startNextTest(); + } else { + info("unexpected message was: " + subject.sourceName + ":" + + subject.lineNumber + "; " + subject.errorMessage); + } + } +}; + +function consoleOpened(hudConsole) { + hud = hudConsole; + output = hud.outputNode; + jsterm = hud.jsterm; + + Services.console.registerListener(TestObserver); + + registerCleanupFunction(testEnd); + + testNext(); +} + +function testNext() { + jsterm.clearOutput(); + foundCategory = false; + foundText = false; + pageLoaded = false; + pageError = false; + + pos++; + info("testNext: #" + pos); + if (pos < TESTS.length) { + test = TESTS[pos]; + + waitForMessages({ + webconsole: hud, + messages: [{ + name: "message for test #" + pos + ": '" + test.matchString + "'", + text: test.matchString, + }], + }).then(() => { + foundText = true; + startNextTest(); + }); + + let testLocation = TESTS_PATH + test.file; + gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) { + if (content.location.href != testLocation) { + return; + } + gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true); + + pageLoaded = true; + test.onload && test.onload(evt); + + if (test.expectError) { + content.addEventListener("error", function _onError() { + content.removeEventListener("error", _onError); + pageError = true; + startNextTest(); + }); + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + } else { + pageError = true; + } + + startNextTest(); + }, true); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, testLocation); + } else { + testEnded = true; + finishTest(); + } +} + +function testEnd() { + if (!testEnded) { + info("foundCategory " + foundCategory + " foundText " + foundText + + " pageLoaded " + pageLoaded + " pageError " + pageError); + } + + Services.console.unregisterListener(TestObserver); + hud = TestObserver = output = jsterm = null; +} + +function startNextTest() { + if (!testEnded && foundCategory && foundText && pageLoaded && pageError) { + testNext(); + } +} + +function test() { + requestLongerTimeout(2); + + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js b/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js new file mode 100644 index 000000000..e14c3a069 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js @@ -0,0 +1,97 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +var tab1, tab2, win1, win2; +var noErrors = true; + +function tab1Loaded() { + win2 = OpenBrowserWindow(); + whenDelayedStartupFinished(win2, win2Loaded); +} + +function win2Loaded() { + tab2 = win2.gBrowser.addTab(TEST_URI); + win2.gBrowser.selectedTab = tab2; + tab2.linkedBrowser.addEventListener("load", tab2Loaded, true); +} + +function tab2Loaded(aEvent) { + tab2.linkedBrowser.removeEventListener(aEvent.type, tab2Loaded, true); + + let consolesOpened = 0; + function onWebConsoleOpen() { + consolesOpened++; + if (consolesOpened == 2) { + executeSoon(closeConsoles); + } + } + + function openConsoles() { + try { + let target1 = TargetFactory.forTab(tab1); + gDevTools.showToolbox(target1, "webconsole").then(onWebConsoleOpen); + } catch (ex) { + ok(false, "gDevTools.showToolbox(target1) exception: " + ex); + noErrors = false; + } + + try { + let target2 = TargetFactory.forTab(tab2); + gDevTools.showToolbox(target2, "webconsole").then(onWebConsoleOpen); + } catch (ex) { + ok(false, "gDevTools.showToolbox(target2) exception: " + ex); + noErrors = false; + } + } + + function closeConsoles() { + try { + let target1 = TargetFactory.forTab(tab1); + gDevTools.closeToolbox(target1).then(function () { + try { + let target2 = TargetFactory.forTab(tab2); + gDevTools.closeToolbox(target2).then(testEnd); + } catch (ex) { + ok(false, "gDevTools.closeToolbox(target2) exception: " + ex); + noErrors = false; + } + }); + } catch (ex) { + ok(false, "gDevTools.closeToolbox(target1) exception: " + ex); + noErrors = false; + } + } + + function testEnd() { + ok(noErrors, "there were no errors"); + + win1.gBrowser.removeTab(tab1); + + Array.forEach(win2.gBrowser.tabs, function (aTab) { + win2.gBrowser.removeTab(aTab); + }); + + executeSoon(function () { + win2.close(); + tab1 = tab2 = win1 = win2 = null; + finishTest(); + }); + } + + openConsoles(); +} + +function test() { + loadTab(TEST_URI).then(() => { + tab1 = gBrowser.selectedTab; + win1 = window; + tab1Loaded(); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js b/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js new file mode 100644 index 000000000..336700ada --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-bug-597136-external-script-" + + "errors.html"; + +function test() { + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + BrowserTestUtils.synthesizeMouseAtCenter("button", {}, gBrowser.selectedBrowser); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bogus is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js b/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js new file mode 100644 index 000000000..473f02ccc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that network requests from chrome don't cause the Web Console to +// throw exceptions. + +"use strict"; + +const TEST_URI = "http://example.com/"; + +var good = true; +var listener = { + QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ]), + observe: function (subject) { + if (subject instanceof Ci.nsIScriptError && + subject.category === "XPConnect JavaScript" && + subject.sourceName.includes("webconsole")) { + good = false; + } + } +}; + +var xhr; + +function test() { + Services.console.registerListener(listener); + + // trigger a lazy-load of the HUD Service + HUDService; + + xhr = new XMLHttpRequest(); + xhr.addEventListener("load", xhrComplete, false); + xhr.open("GET", TEST_URI, true); + xhr.send(null); +} + +function xhrComplete() { + xhr.removeEventListener("load", xhrComplete, false); + window.setTimeout(checkForException, 0); +} + +function checkForException() { + ok(good, "no exception was thrown when sending a network request from a " + + "chrome window"); + + Services.console.unregisterListener(listener); + listener = xhr = null; + + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js new file mode 100644 index 000000000..2de4c9f21 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js @@ -0,0 +1,80 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-network.html"; +const PREF = "devtools.webconsole.persistlog"; + +add_task(function* () { + Services.prefs.setBoolPref(PREF, true); + + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let results = yield consoleOpened(hud); + + testScroll(results, hud); + + Services.prefs.clearUserPref(PREF); +}); + +function consoleOpened(hud) { + let deferred = promise.defer(); + + for (let i = 0; i < 200; i++) { + content.console.log("test message " + i); + } + + hud.setFilterState("network", false); + hud.setFilterState("networkinfo", false); + + hud.ui.filterBox.value = "test message"; + hud.ui.adjustVisibilityOnSearchStringChange(); + + waitForMessages({ + webconsole: hud, + messages: [{ + name: "console messages displayed", + text: "test message 199", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + waitForMessages({ + webconsole: hud, + messages: [{ + text: "test-network.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }).then(deferred.resolve); + + content.location.reload(); + }); + + return deferred.promise; +} + +function testScroll([result], hud) { + let scrollNode = hud.ui.outputWrapper; + let msgNode = [...result.matched][0]; + ok(msgNode.classList.contains("filtered-by-type"), + "network message is filtered by type"); + ok(msgNode.classList.contains("filtered-by-string"), + "network message is filtered by string"); + + ok(scrollNode.scrollTop > 0, "scroll location is not at the top"); + + // Make sure the Web Console output is scrolled as near as possible to the + // bottom. + let nodeHeight = msgNode.clientHeight; + ok(scrollNode.scrollTop >= scrollNode.scrollHeight - scrollNode.clientHeight - + nodeHeight * 2, "scroll location is correct"); + + hud.setFilterState("network", true); + hud.setFilterState("networkinfo", true); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js b/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js new file mode 100644 index 000000000..5a8280eed --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js @@ -0,0 +1,70 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-597756-reopen-closed-tab.html"; + +var HUD; + +add_task(function* () { + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + let { browser } = yield loadTab(TEST_URI); + HUD = yield openConsole(); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + yield reload(browser); + + yield testMessages(); + + yield closeConsole(); + + // Close and reopen + gBrowser.removeCurrentTab(); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + let tab = yield loadTab(TEST_URI); + HUD = yield openConsole(); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + yield reload(tab.browser); + + yield testMessages(); + + HUD = null; +}); + +function reload(browser) { + let loaded = loadBrowser(browser); + browser.reload(); + return loaded; +} + +function testMessages() { + return waitForMessages({ + webconsole: HUD, + messages: [{ + name: "error message displayed", + text: "fooBug597756_error", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js b/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js new file mode 100644 index 000000000..4849793cb --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const INIT_URI = "data:text/plain;charset=utf8,hello world"; +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-599725-response-headers.sjs"; + +function performTest(request, hud) { + let deferred = promise.defer(); + + let headers = null; + + function readHeader(name) { + for (let header of headers) { + if (header.name == name) { + return header.value; + } + } + return null; + } + + hud.ui.proxy.webConsoleClient.getResponseHeaders(request.actor, + function (response) { + headers = response.headers; + ok(headers, "we have the response headers for reload"); + + let contentType = readHeader("Content-Type"); + let contentLength = readHeader("Content-Length"); + + ok(!contentType, "we do not have the Content-Type header"); + isnot(contentLength, 60, "Content-Length != 60"); + + executeSoon(deferred.resolve); + }); + + return deferred.promise; +} + +let waitForRequest = Task.async(function*(hud) { + let request = yield waitForFinishedRequest(req=> { + return req.response.status === "304"; + }); + + yield performTest(request, hud); +}); + +add_task(function* () { + let { browser } = yield loadTab(INIT_URI); + + let hud = yield openConsole(); + + let gotLastRequest = waitForRequest(hud); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_URI); + yield loaded; + + let reloaded = loadBrowser(browser); + ContentTask.spawn(browser, null, "() => content.location.reload()"); + yield reloaded; + + yield gotLastRequest; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js b/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js new file mode 100644 index 000000000..153863824 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js @@ -0,0 +1,59 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const INIT_URI = "data:text/html;charset=utf-8,Web Console - bug 600183 test"; +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-600183-charset.html"; + +function performTest(lastFinishedRequest, console) { + let deferred = promise.defer(); + + ok(lastFinishedRequest, "charset test page was loaded and logged"); + HUDService.lastFinishedRequest.callback = null; + + executeSoon(() => { + console.webConsoleClient.getResponseContent(lastFinishedRequest.actor, + (response) => { + ok(!response.contentDiscarded, "response body was not discarded"); + + let body = response.content.text; + ok(body, "we have the response body"); + + // 的问候! + let chars = "\u7684\u95ee\u5019!"; + isnot(body.indexOf("<p>" + chars + "</p>"), -1, + "found the chinese simplified string"); + + HUDService.lastFinishedRequest.callback = null; + executeSoon(deferred.resolve); + }); + }); + + return deferred.promise; +} + +function waitForRequest() { + let deferred = promise.defer(); + HUDService.lastFinishedRequest.callback = (req, console) => { + performTest(req, console).then(deferred.resolve); + }; + return deferred.promise; +} + +add_task(function* () { + let { browser } = yield loadTab(INIT_URI); + + yield openConsole(); + + let gotLastRequest = waitForRequest(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_URI); + yield loaded; + + yield gotLastRequest; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js b/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js new file mode 100644 index 000000000..9dd81c9fd --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 601177: log levels"; +const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-601177-log-levels.html"; + +add_task(function* () { + Services.prefs.setBoolPref("javascript.options.strict", true); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + yield testLogLevels(hud); + + Services.prefs.clearUserPref("javascript.options.strict"); +}); + +function testLogLevels(hud) { + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2); + + info("waiting for messages"); + + return waitForMessages({ + webconsole: hud, + messages: [ + { + text: "test-bug-601177-log-levels.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "test-bug-601177-log-levels.js", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "test-image.png", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "foobar-known-to-fail.png", + category: CATEGORY_NETWORK, + severity: SEVERITY_ERROR, + }, + { + text: "foobarBug601177exception", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "undefinedPropertyBug601177", + category: CATEGORY_JS, + severity: SEVERITY_WARNING, + }, + { + text: "foobarBug601177strictError", + category: CATEGORY_JS, + severity: SEVERITY_WARNING, + }, + ], + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js new file mode 100644 index 000000000..89bd83a7a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js @@ -0,0 +1,84 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the console output scrolls to JS eval results when there are many +// messages displayed. See bug 601352. + +"use strict"; + +add_task(function* () { + let {tab} = yield loadTab("data:text/html;charset=utf-8,Web Console test " + + "for bug 601352"); + let hud = yield openConsole(tab); + hud.jsterm.clearOutput(); + + yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let longMessage = ""; + for (let i = 0; i < 50; i++) { + longMessage += "LongNonwrappingMessage"; + } + + for (let i = 0; i < 50; i++) { + content.console.log("test1 message " + i); + } + + content.console.log(longMessage); + + for (let i = 0; i < 50; i++) { + content.console.log("test2 message " + i); + } + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test1 message 0", + }, { + text: "test1 message 49", + }, { + text: "LongNonwrappingMessage", + }, { + text: "test2 message 0", + }, { + text: "test2 message 49", + }], + }); + + let node = yield hud.jsterm.execute("1+1"); + + let scrollNode = hud.ui.outputWrapper; + let rectNode = node.getBoundingClientRect(); + let rectOutput = scrollNode.getBoundingClientRect(); + + yield ContentTask.spawn(gBrowser.selectedBrowser, { + rectNode, + rectOutput, + scrollHeight: scrollNode.scrollHeight, + scrollTop: scrollNode.scrollTop, + clientHeight: scrollNode.clientHeight, + }, function* (args) { + console.debug("rectNode", args.rectNode, "rectOutput", args.rectOutput); + console.log("scrollNode scrollHeight", args.scrollHeight, + "scrollTop", args.scrollTop, "clientHeight", + args.clientHeight); + }); + + isnot(scrollNode.scrollTop, 0, "scroll location is not at the top"); + + // The bounding client rect .top/left coordinates are relative to the + // console iframe. + + // Visible scroll viewport. + let height = rectOutput.height; + + // Top and bottom coordinates of the last message node, relative to the + // outputNode. + let top = rectNode.top - rectOutput.top; + let bottom = top + rectNode.height; + info("node top " + top + " node bottom " + bottom + " node clientHeight " + + node.clientHeight); + + ok(top >= 0 && bottom <= height, "last message is visible"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js b/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js new file mode 100644 index 000000000..6dae0a7b7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js @@ -0,0 +1,267 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the filter button UI logic works correctly. + +"use strict"; + +const TEST_URI = "http://example.com/"; +const FILTER_PREF_DOMAIN = "devtools.webconsole.filter."; + +var hud, hudId, hudBox; +var prefs = {}; + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + hudId = hud.hudId; + hudBox = hud.ui.rootElement; + + savePrefs(); + + testFilterButtons(); + + restorePrefs(); + + hud = hudId = hudBox = null; +}); + +function savePrefs() { + let branch = Services.prefs.getBranch(FILTER_PREF_DOMAIN); + let children = branch.getChildList(""); + for (let child of children) { + prefs[child] = branch.getBoolPref(child); + } +} + +function restorePrefs() { + let branch = Services.prefs.getBranch(FILTER_PREF_DOMAIN); + for (let p in prefs) { + branch.setBoolPref(p, prefs[p]); + } +} + +function testFilterButtons() { + testMenuFilterButton("net"); + testMenuFilterButton("css"); + testMenuFilterButton("js"); + testMenuFilterButton("logging"); + testMenuFilterButton("security"); + testMenuFilterButton("server"); + + testIsolateFilterButton("net"); + testIsolateFilterButton("css"); + testIsolateFilterButton("js"); + testIsolateFilterButton("logging"); + testIsolateFilterButton("security"); + testIsolateFilterButton("server"); +} + +function testMenuFilterButton(category) { + let selector = ".webconsole-filter-button[category=\"" + category + "\"]"; + let button = hudBox.querySelector(selector); + ok(button, "we have the \"" + category + "\" button"); + + let firstMenuItem = button.querySelector("menuitem"); + ok(firstMenuItem, "we have the first menu item for the \"" + category + + "\" button"); + + // Turn all the filters off, if they were on. + let menuItem = firstMenuItem; + while (menuItem != null) { + if (menuItem.hasAttribute("prefKey") && isChecked(menuItem)) { + chooseMenuItem(menuItem); + } + menuItem = menuItem.nextSibling; + } + + // Turn all the filters on; make sure the button gets checked. + menuItem = firstMenuItem; + let prefKey; + while (menuItem) { + if (menuItem.hasAttribute("prefKey")) { + prefKey = menuItem.getAttribute("prefKey"); + chooseMenuItem(menuItem); + ok(isChecked(menuItem), "menu item " + prefKey + " for category " + + category + " is checked after clicking it"); + ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "on after clicking the appropriate menu item"); + } + menuItem = menuItem.nextSibling; + } + ok(isChecked(button), "the button for category " + category + " is " + + "checked after turning on all its menu items"); + + // Turn one filter off; make sure the button is still checked. + prefKey = firstMenuItem.getAttribute("prefKey"); + chooseMenuItem(firstMenuItem); + ok(!isChecked(firstMenuItem), "the first menu item for category " + + category + " is no longer checked after clicking it"); + ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "turned off after clicking the appropriate menu item"); + ok(isChecked(button), "the button for category " + category + " is still " + + "checked after turning off its first menu item"); + + // Turn all the filters off by clicking the main part of the button. + let subbutton = getMainButton(button); + ok(subbutton, "we have the subbutton for category " + category); + + clickButton(subbutton); + ok(!isChecked(button), "the button for category " + category + " is " + + "no longer checked after clicking its main part"); + + menuItem = firstMenuItem; + while (menuItem) { + prefKey = menuItem.getAttribute("prefKey"); + if (prefKey) { + ok(!isChecked(menuItem), "menu item " + prefKey + " for category " + + category + " is no longer checked after clicking the button"); + ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "off after clicking the button"); + } + menuItem = menuItem.nextSibling; + } + + // Turn all the filters on by clicking the main part of the button. + clickButton(subbutton); + + ok(isChecked(button), "the button for category " + category + " is " + + "checked after clicking its main part"); + + menuItem = firstMenuItem; + while (menuItem) { + if (menuItem.hasAttribute("prefKey")) { + prefKey = menuItem.getAttribute("prefKey"); + // The CSS/Log menu item should not be checked. See bug 971798. + if (category == "css" && prefKey == "csslog") { + ok(!isChecked(menuItem), "menu item " + prefKey + " for category " + + category + " should not be checked after clicking the button"); + ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "off after clicking the button"); + } else { + ok(isChecked(menuItem), "menu item " + prefKey + " for category " + + category + " is checked after clicking the button"); + ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "on after clicking the button"); + } + } + menuItem = menuItem.nextSibling; + } + + // Uncheck the main button by unchecking all the filters + menuItem = firstMenuItem; + while (menuItem) { + // The csslog menu item is already unchecked at this point. + // Make sure it is not selected. See bug 971798. + prefKey = menuItem.getAttribute("prefKey"); + if (prefKey && prefKey != "csslog") { + chooseMenuItem(menuItem); + } + menuItem = menuItem.nextSibling; + } + + ok(!isChecked(button), "the button for category " + category + " is " + + "unchecked after unchecking all its filters"); + + // Turn all the filters on again by clicking the button. + clickButton(subbutton); +} + +function testIsolateFilterButton(category) { + let selector = ".webconsole-filter-button[category=\"" + category + "\"]"; + let targetButton = hudBox.querySelector(selector); + ok(targetButton, "we have the \"" + category + "\" button"); + + // Get the main part of the filter button. + let subbutton = getMainButton(targetButton); + ok(subbutton, "we have the subbutton for category " + category); + + // Turn on all the filters by alt clicking the main part of the button. + altClickButton(subbutton); + ok(isChecked(targetButton), "the button for category " + category + + " is checked after isolating for filter"); + + // Check if all the filters for the target button are on. + let menuItems = targetButton.querySelectorAll("menuitem"); + Array.forEach(menuItems, (item) => { + let prefKey = item.getAttribute("prefKey"); + // The CSS/Log filter should not be checked. See bug 971798. + if (category == "css" && prefKey == "csslog") { + ok(!isChecked(item), "menu item " + prefKey + " for category " + + category + " should not be checked after isolating for " + category); + ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages should be " + + "turned off after isolating for " + category); + } else if (prefKey) { + ok(isChecked(item), "menu item " + prefKey + " for category " + + category + " is checked after isolating for " + category); + ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "turned on after isolating for " + category); + } + }); + + // Ensure all other filter buttons are toggled off and their + // associated filters are turned off + let buttons = hudBox.querySelectorAll(".webconsole-filter-button[category]"); + Array.forEach(buttons, (filterButton) => { + if (filterButton !== targetButton) { + let categoryBtn = filterButton.getAttribute("category"); + ok(!isChecked(filterButton), "the button for category " + + categoryBtn + " is unchecked after isolating for " + category); + + menuItems = filterButton.querySelectorAll("menuitem"); + Array.forEach(menuItems, (item) => { + let prefKey = item.getAttribute("prefKey"); + if (prefKey) { + ok(!isChecked(item), "menu item " + prefKey + " for category " + + category + " is unchecked after isolating for " + category); + ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " + + "turned off after isolating for " + category); + } + }); + + // Turn all the filters on again by clicking the button. + let mainButton = getMainButton(filterButton); + clickButton(mainButton); + } + }); +} + +/** + * Return the main part of the target filter button. + */ +function getMainButton(targetButton) { + let anonymousNodes = hud.ui.document.getAnonymousNodes(targetButton); + let subbutton; + + for (let i = 0; i < anonymousNodes.length; i++) { + let node = anonymousNodes[i]; + if (node.classList.contains("toolbarbutton-menubutton-button")) { + subbutton = node; + break; + } + } + + return subbutton; +} + +function clickButton(node) { + EventUtils.sendMouseEvent({ type: "click" }, node); +} + +function altClickButton(node) { + EventUtils.sendMouseEvent({ type: "click", altKey: true }, node); +} + +function chooseMenuItem(node) { + let event = document.createEvent("XULCommandEvent"); + event.initCommandEvent("command", true, true, window, 0, false, false, false, + false, null); + node.dispatchEvent(event); +} + +function isChecked(node) { + return node.getAttribute("checked") === "true"; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js b/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js new file mode 100644 index 000000000..f14530d06 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-603750-websocket.html"; +const TEST_URI2 = "data:text/html;charset=utf-8,Web Console test for " + + "bug 603750: Web Socket errors"; + +add_task(function* () { + yield loadTab(TEST_URI2); + + let hud = yield openConsole(); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "ws://0.0.0.0:81", + source: { url: "test-bug-603750-websocket.js" }, + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "ws://0.0.0.0:82", + source: { url: "test-bug-603750-websocket.js" }, + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + ] + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_611795.js b/devtools/client/webconsole/test/browser_webconsole_bug_611795.js new file mode 100644 index 000000000..1fa4d717e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_611795.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = 'data:text/html;charset=utf-8,<div style="-moz-opacity:0;">' + + 'test repeated css warnings</div><p style="-moz-opacity:0">' + + "hi</p>"; +var hud; + +/** + * Unit test for bug 611795: + * Repeated CSS messages get collapsed into one. + */ + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + hud.jsterm.clearOutput(true); + + BrowserReload(); + yield loadBrowser(gBrowser.selectedBrowser); + + yield onContentLoaded(); + yield testConsoleLogRepeats(); + + hud = null; +}); + +function onContentLoaded() { + let cssWarning = "Unknown property \u2018-moz-opacity\u2019. Declaration dropped."; + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: cssWarning, + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + repeats: 2, + }], + }); +} + +function testConsoleLogRepeats() { + let jsterm = hud.jsterm; + + jsterm.clearOutput(); + + jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('this is a " + + "line of reasonably long text that I will use to " + + "verify that the repeated text node is of an " + + "appropriate size.');"); + jsterm.execute(); + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "this is a line of reasonably long text", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + repeats: 10, + }], + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js b/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js new file mode 100644 index 000000000..5d0067958 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js @@ -0,0 +1,26 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-613013-console-api-iframe.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + BrowserReload(); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarBug613013", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js new file mode 100644 index 000000000..95752021d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js @@ -0,0 +1,64 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 613280"; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then((HUD) => { + ContentTask.spawn(gBrowser.selectedBrowser, null, function*(){ + content.console.log("foobarBazBug613280"); + }); + waitForMessages({ + webconsole: HUD, + messages: [{ + text: "foobarBazBug613280", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(performTest.bind(null, HUD)); + }); + }); +} + +function performTest(HUD, [result]) { + let msg = [...result.matched][0]; + let input = HUD.jsterm.inputNode; + + let clipboardSetup = function () { + goDoCommand("cmd_copy"); + }; + + let clipboardCopyDone = function () { + finishTest(); + }; + + let controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled"); + + HUD.ui.output.selectMessage(msg); + HUD.outputNode.focus(); + + goUpdateCommand("cmd_copy"); + + controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); + + // Remove new lines and whitespace since getSelection() includes + // a new line between message and line number, but the clipboard doesn't + // @see bug 1119503 + let selectionText = (HUD.iframeWindow.getSelection() + "") + .replace(/\r?\n|\r| /g, ""); + isnot(selectionText.indexOf("foobarBazBug613280"), -1, + "selection text includes 'foobarBazBug613280'"); + + waitForClipboard((str) => { + return selectionText.trim() === str.trim().replace(/ /g, ""); + }, clipboardSetup, clipboardCopyDone, clipboardCopyDone); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js new file mode 100644 index 000000000..e24ce28e2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js @@ -0,0 +1,119 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +var TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 613642: remember scroll location"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + let outputNode = hud.outputNode; + let scrollBox = hud.ui.outputWrapper; + + for (let i = 0; i < 150; i++) { + ContentTask.spawn(gBrowser.selectedBrowser, i, function* (num) { + content.console.log("test message " + num); + }); + } + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 149", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + ok(scrollBox.scrollTop > 0, "scroll location is not at the top"); + + // scroll to the first node + outputNode.focus(); + + let scrolled = promise.defer(); + + scrollBox.onscroll = () => { + info("onscroll top " + scrollBox.scrollTop); + if (scrollBox.scrollTop != 0) { + // Wait for scroll to 0. + return; + } + scrollBox.onscroll = null; + is(scrollBox.scrollTop, 0, "scroll location updated (moved to top)"); + scrolled.resolve(); + }; + EventUtils.synthesizeKey("VK_HOME", {}, hud.iframeWindow); + + yield scrolled.promise; + + // add a message and make sure scroll doesn't change + ContentTask.spawn(gBrowser.selectedBrowser, null, + "() => content.console.log('test message 150')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 150", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + scrolled = promise.defer(); + scrollBox.onscroll = () => { + if (scrollBox.scrollTop != 0) { + // Wait for scroll to stabilize at the top. + return; + } + scrollBox.onscroll = null; + is(scrollBox.scrollTop, 0, "scroll location is still at the top"); + scrolled.resolve(); + }; + + // Make sure that scroll stabilizes at the top. executeSoon() is needed for + // the yield to work. + executeSoon(scrollBox.onscroll); + + yield scrolled.promise; + + // scroll back to the bottom + outputNode.lastChild.focus(); + + scrolled = promise.defer(); + scrollBox.onscroll = () => { + if (scrollBox.scrollTop == 0) { + // Wait for scroll to bottom. + return; + } + scrollBox.onscroll = null; + isnot(scrollBox.scrollTop, 0, "scroll location updated (moved to bottom)"); + scrolled.resolve(); + }; + EventUtils.synthesizeKey("VK_END", {}); + yield scrolled.promise; + + let oldScrollTop = scrollBox.scrollTop; + + ContentTask.spawn(gBrowser.selectedBrowser, null, + "() => content.console.log('test message 151')"); + + scrolled = promise.defer(); + scrollBox.onscroll = () => { + if (scrollBox.scrollTop == oldScrollTop) { + // Wait for scroll to change. + return; + } + scrollBox.onscroll = null; + isnot(scrollBox.scrollTop, oldScrollTop, + "scroll location updated (moved to bottom again)"); + scrolled.resolve(); + }; + yield scrolled.promise; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js new file mode 100644 index 000000000..c53fe1683 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js @@ -0,0 +1,82 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 613642: maintain scroll with pruning of old messages"; + +var hud; + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + + hud.jsterm.clearOutput(); + + let outputNode = hud.outputNode; + + Services.prefs.setIntPref("devtools.hud.loglimit.console", 140); + let scrollBoxElement = hud.ui.outputWrapper; + + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + for (let i = 0; i < 150; i++) { + content.console.log("test message " + i); + } + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 149", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let oldScrollTop = scrollBoxElement.scrollTop; + isnot(oldScrollTop, 0, "scroll location is not at the top"); + + let firstNode = outputNode.firstChild; + ok(firstNode, "found the first message"); + + let msgNode = outputNode.children[80]; + ok(msgNode, "found the 80th message"); + + // scroll to the middle message node + msgNode.scrollIntoView(false); + + isnot(scrollBoxElement.scrollTop, oldScrollTop, + "scroll location updated (scrolled to message)"); + + oldScrollTop = scrollBoxElement.scrollTop; + + // add a message + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.console.log("hello world"); + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "hello world", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + // Scroll location needs to change, because one message is also removed, and + // we need to scroll a bit towards the top, to keep the current view in sync. + isnot(scrollBoxElement.scrollTop, oldScrollTop, + "scroll location updated (added a message)"); + + isnot(outputNode.firstChild, firstNode, + "first message removed"); + + Services.prefs.clearUserPref("devtools.hud.loglimit.console"); + + hud = null; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js new file mode 100644 index 000000000..ae61023a9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 614793: jsterm result scroll"; + +requestLongerTimeout(2); + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + yield testScrollPosition(hud); +}); + +function* testScrollPosition(hud) { + hud.jsterm.clearOutput(); + + let scrollNode = hud.ui.outputWrapper; + + for (let i = 0; i < 150; i++) { + yield ContentTask.spawn(gBrowser.selectedBrowser, i, function* (i) { + content.console.log("test message " + i); + }); + } + + let oldScrollTop = -1; + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 149", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + oldScrollTop = scrollNode.scrollTop; + isnot(oldScrollTop, 0, "scroll location is not at the top"); + + let msg = yield hud.jsterm.execute("'hello world'"); + + isnot(scrollNode.scrollTop, oldScrollTop, "scroll location updated"); + + oldScrollTop = scrollNode.scrollTop; + + msg.scrollIntoView(false); + + is(scrollNode.scrollTop, oldScrollTop, "scroll location is the same"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js b/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js new file mode 100644 index 000000000..439793b22 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that we report JS exceptions in event handlers coming from +// network requests, like onreadystate for XHR. See bug 618078. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 618078"; +const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-618078-network-exceptions.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug618078exception", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js b/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js new file mode 100644 index 000000000..6f4248c51 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-621644-jsterm-dollar.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield test$(hud); + yield test$$(hud); +}); + +function* test$(HUD) { + let deferred = promise.defer(); + + HUD.jsterm.clearOutput(); + + HUD.jsterm.execute("$(document.body)", (msg) => { + ok(msg.textContent.indexOf("<p>") > -1, + "jsterm output is correct for $()"); + deferred.resolve(); + }); + + return deferred.promise; +} + +function test$$(HUD) { + let deferred = promise.defer(); + + HUD.jsterm.clearOutput(); + + HUD.jsterm.setInputValue(); + HUD.jsterm.execute("$$(document)", (msg) => { + ok(msg.textContent.indexOf("621644") > -1, + "jsterm output is correct for $$()"); + deferred.resolve(); + }); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js b/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js new file mode 100644 index 000000000..f4b5dca96 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js @@ -0,0 +1,149 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const prefs = { + "net": [ + "network", + "netwarn", + "netxhr", + "networkinfo" + ], + "css": [ + "csserror", + "cssparser", + "csslog" + ], + "js": [ + "exception", + "jswarn", + "jslog", + ], + "logging": [ + "error", + "warn", + "info", + "log", + "serviceworkers", + "sharedworkers", + "windowlessworkers" + ] +}; + +add_task(function* () { + // Set all prefs to true + for (let category in prefs) { + prefs[category].forEach(function (pref) { + Services.prefs.setBoolPref("devtools.webconsole.filter." + pref, true); + }); + } + + yield loadTab("about:blank"); + + let hud = yield openConsole(); + + let hud2 = yield onConsoleOpen(hud); + let hud3 = yield onConsoleReopen1(hud2); + yield onConsoleReopen2(hud3); + + // Clear prefs + for (let category in prefs) { + prefs[category].forEach(function (pref) { + Services.prefs.clearUserPref("devtools.webconsole.filter." + pref); + }); + } +}); + +function onConsoleOpen(hud) { + let deferred = promise.defer(); + + let hudBox = hud.ui.rootElement; + + // Check if the filters menuitems exists and are checked + for (let category in prefs) { + let button = hudBox.querySelector(".webconsole-filter-button[category=\"" + + category + "\"]"); + ok(isChecked(button), "main button for " + category + + " category is checked"); + + prefs[category].forEach(function (pref) { + let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]"); + ok(isChecked(menuitem), "menuitem for " + pref + " is checked"); + }); + } + + // Set all prefs to false + for (let category in prefs) { + prefs[category].forEach(function (pref) { + hud.setFilterState(pref, false); + }); + } + + // Re-init the console + closeConsole().then(() => { + openConsole().then(deferred.resolve); + }); + + return deferred.promise; +} + +function onConsoleReopen1(hud) { + info("testing after reopening once"); + let deferred = promise.defer(); + + let hudBox = hud.ui.rootElement; + + // Check if the filter button and menuitems are unchecked + for (let category in prefs) { + let button = hudBox.querySelector(".webconsole-filter-button[category=\"" + + category + "\"]"); + ok(isUnchecked(button), "main button for " + category + + " category is not checked"); + + prefs[category].forEach(function (pref) { + let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]"); + ok(isUnchecked(menuitem), "menuitem for " + pref + " is not checked"); + }); + } + + // Set first pref in each category to true + for (let category in prefs) { + hud.setFilterState(prefs[category][0], true); + } + + // Re-init the console + closeConsole().then(() => { + openConsole().then(deferred.resolve); + }); + + return deferred.promise; +} + +function onConsoleReopen2(hud) { + info("testing after reopening again"); + + let hudBox = hud.ui.rootElement; + + // Check the main category button is checked and first menuitem is checked + for (let category in prefs) { + let button = hudBox.querySelector(".webconsole-filter-button[category=\"" + + category + "\"]"); + ok(isChecked(button), category + + " button is checked when first pref is true"); + + let pref = prefs[category][0]; + let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]"); + ok(isChecked(menuitem), "first " + category + " menuitem is checked"); + } +} + +function isChecked(aNode) { + return aNode.getAttribute("checked") === "true"; +} + +function isUnchecked(aNode) { + return aNode.getAttribute("checked") === "false"; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js b/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js new file mode 100644 index 000000000..3de10774d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test for https://bugzilla.mozilla.org/show_bug.cgi?id=623749 +// Map Control + A to Select All, In the web console input, on Windows + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Test console for bug 623749"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let jsterm = hud.jsterm; + jsterm.setInputValue("Ignore These Four Words"); + let inputNode = jsterm.inputNode; + + // Test select all with Control + A. + EventUtils.synthesizeKey("a", { ctrlKey: true }); + let inputLength = inputNode.selectionEnd - inputNode.selectionStart; + is(inputLength, jsterm.getInputValue().length, "Select all of input"); + + // Test do nothing on Control + E. + jsterm.setInputValue("Ignore These Four Words"); + inputNode.selectionStart = 0; + EventUtils.synthesizeKey("e", { ctrlKey: true }); + is(inputNode.selectionStart, 0, "Control + E does not move to end of input"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js new file mode 100644 index 000000000..509749953 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js @@ -0,0 +1,120 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " + + "bug 630733"; +const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-630733-response-redirect-headers.sjs"; + +var lastFinishedRequests = {}; +var webConsoleClient; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield consoleOpened(hud); + yield getHeaders(); + yield getContent(); + + performTest(); +}); + +function consoleOpened(hud) { + let deferred = promise.defer(); + + webConsoleClient = hud.ui.webConsoleClient; + HUDService.lastFinishedRequest.callback = (aHttpRequest) => { + let status = aHttpRequest.response.status; + lastFinishedRequests[status] = aHttpRequest; + if ("301" in lastFinishedRequests && + "404" in lastFinishedRequests) { + deferred.resolve(); + } + }; + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2); + + return deferred.promise; +} + +function getHeaders() { + let deferred = promise.defer(); + + HUDService.lastFinishedRequest.callback = null; + + ok("301" in lastFinishedRequests, "request 1: 301 Moved Permanently"); + ok("404" in lastFinishedRequests, "request 2: 404 Not found"); + + webConsoleClient.getResponseHeaders(lastFinishedRequests["301"].actor, + function (response) { + lastFinishedRequests["301"].response.headers = response.headers; + + webConsoleClient.getResponseHeaders(lastFinishedRequests["404"].actor, + function (resp) { + lastFinishedRequests["404"].response.headers = resp.headers; + executeSoon(deferred.resolve); + }); + }); + return deferred.promise; +} + +function getContent() { + let deferred = promise.defer(); + + webConsoleClient.getResponseContent(lastFinishedRequests["301"].actor, + function (response) { + lastFinishedRequests["301"].response.content = response.content; + lastFinishedRequests["301"].discardResponseBody = response.contentDiscarded; + + webConsoleClient.getResponseContent(lastFinishedRequests["404"].actor, + function (resp) { + lastFinishedRequests["404"].response.content = resp.content; + lastFinishedRequests["404"].discardResponseBody = + resp.contentDiscarded; + + webConsoleClient = null; + executeSoon(deferred.resolve); + }); + }); + return deferred.promise; +} + +function performTest() { + function readHeader(name) { + for (let header of headers) { + if (header.name == name) { + return header.value; + } + } + return null; + } + + let headers = lastFinishedRequests["301"].response.headers; + is(readHeader("Content-Type"), "text/html", + "we do have the Content-Type header"); + is(readHeader("Content-Length"), 71, "Content-Length is correct"); + is(readHeader("Location"), "/redirect-from-bug-630733", + "Content-Length is correct"); + is(readHeader("x-foobar-bug630733"), "bazbaz", + "X-Foobar-bug630733 is correct"); + + let body = lastFinishedRequests["301"].response.content; + ok(!body.text, "body discarded for request 1"); + ok(lastFinishedRequests["301"].discardResponseBody, + "body discarded for request 1 (confirmed)"); + + headers = lastFinishedRequests["404"].response.headers; + ok(!readHeader("Location"), "no Location header"); + ok(!readHeader("x-foobar-bug630733"), "no X-Foobar-bug630733 header"); + + body = lastFinishedRequests["404"].response.content.text; + isnot(body.indexOf("404"), -1, + "body is correct for request 2"); + + lastFinishedRequests = webConsoleClient = null; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js b/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js new file mode 100644 index 000000000..45d1f7102 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-632275-getters.html"; + +var getterValue = null; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} + +function consoleOpened(hud) { + let doc = content.wrappedJSObject.document; + getterValue = doc.foobar._val; + hud.jsterm.execute("console.dir(document)"); + + let onOpen = onViewOpened.bind(null, hud); + hud.jsterm.once("variablesview-fetched", onOpen); +} + +function onViewOpened(hud, event, view) { + let doc = content.wrappedJSObject.document; + + findVariableViewProperties(view, [ + { name: /^(width|height)$/, dontMatch: 1 }, + { name: "foobar._val", value: getterValue }, + { name: "foobar.val", isGetter: true }, + ], { webconsole: hud }).then(function () { + is(doc.foobar._val, getterValue, "getter did not execute"); + is(doc.foobar.val, getterValue + 1, "getter executed"); + is(doc.foobar._val, getterValue + 1, "getter executed (recheck)"); + + let textContent = hud.outputNode.textContent; + is(textContent.indexOf("document.body.client"), -1, + "no document.width/height warning displayed"); + + getterValue = null; + finishTest(); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js new file mode 100644 index 000000000..c5e672444 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js @@ -0,0 +1,84 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-632347-iterators-generators.html"; + +function test() { + requestLongerTimeout(6); + + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} + +function consoleOpened(HUD) { + let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider"); + + let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + tmp.addDebuggerToGlobal(tmp); + let dbg = new tmp.Debugger(); + + let jsterm = HUD.jsterm; + let win = content.wrappedJSObject; + let dbgWindow = dbg.addDebuggee(content); + let container = win._container; + + // Make sure autocomplete does not walk through iterators and generators. + let result = container.gen1.next(); + let completion = JSPropertyProvider(dbgWindow, null, "_container.gen1."); + isnot(completion.matches.length, 0, "Got matches for gen1"); + + is(result + 1, container.gen1.next(), "gen1.next() did not execute"); + + result = container.gen2.next().value; + + completion = JSPropertyProvider(dbgWindow, null, "_container.gen2."); + isnot(completion.matches.length, 0, "Got matches for gen2"); + + is((result / 2 + 1) * 2, container.gen2.next().value, + "gen2.next() did not execute"); + + result = container.iter1.next(); + is(result[0], "foo", "iter1.next() [0] is correct"); + is(result[1], "bar", "iter1.next() [1] is correct"); + + completion = JSPropertyProvider(dbgWindow, null, "_container.iter1."); + isnot(completion.matches.length, 0, "Got matches for iter1"); + + result = container.iter1.next(); + is(result[0], "baz", "iter1.next() [0] is correct"); + is(result[1], "baaz", "iter1.next() [1] is correct"); + + let dbgContent = dbg.makeGlobalObjectReference(content); + completion = JSPropertyProvider(dbgContent, null, "_container.iter2."); + isnot(completion.matches.length, 0, "Got matches for iter2"); + + completion = JSPropertyProvider(dbgWindow, null, "window._container."); + ok(completion, "matches available for window._container"); + ok(completion.matches.length, "matches available for window (length)"); + + dbg.removeDebuggee(content); + jsterm.clearOutput(); + + jsterm.execute("window._container", (msg) => { + jsterm.once("variablesview-fetched", testVariablesView.bind(null, HUD)); + let anchor = msg.querySelector(".message-body a"); + EventUtils.synthesizeMouse(anchor, 2, 2, {}, HUD.iframeWindow); + }); +} + +function testVariablesView(aWebconsole, aEvent, aView) { + findVariableViewProperties(aView, [ + { name: "gen1", isGenerator: true }, + { name: "gen2", isGenerator: true }, + { name: "iter1", isIterator: true }, + { name: "iter2", isIterator: true }, + ], { webconsole: aWebconsole }).then(function () { + executeSoon(finishTest); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632817.js b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js new file mode 100644 index 000000000..561e3b112 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js @@ -0,0 +1,217 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that Web console messages can be filtered for NET events. + +"use strict"; + +const TEST_NETWORK_REQUEST_URI = + "https://example.com/browser/devtools/client/webconsole/test/" + + "test-network-request.html"; + +const TEST_IMG = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-image.png"; + +const TEST_DATA_JSON_CONTENT = + '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }'; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console network logging " + + "tests"; + +const PAGE_REQUEST_PREDICATE = + ({ request }) => request.url.endsWith("test-network-request.html"); + +const TEST_DATA_REQUEST_PREDICATE = + ({ request }) => request.url.endsWith("test-data.json"); + +const XHR_WARN_REQUEST_PREDICATE = + ({ request }) => request.url.endsWith("sjs_cors-test-server.sjs"); + +let hud; + +add_task(function*() { + const PREF = "devtools.webconsole.persistlog"; + const NET_PREF = "devtools.webconsole.filter.networkinfo"; + const NETXHR_PREF = "devtools.webconsole.filter.netxhr"; + const MIXED_AC_PREF = "security.mixed_content.block_active_content"; + let original = Services.prefs.getBoolPref(NET_PREF); + let originalXhr = Services.prefs.getBoolPref(NETXHR_PREF); + let originalMixedActive = Services.prefs.getBoolPref(MIXED_AC_PREF); + Services.prefs.setBoolPref(NET_PREF, true); + Services.prefs.setBoolPref(NETXHR_PREF, true); + Services.prefs.setBoolPref(MIXED_AC_PREF, false); + Services.prefs.setBoolPref(PREF, true); + registerCleanupFunction(() => { + Services.prefs.setBoolPref(NET_PREF, original); + Services.prefs.setBoolPref(NETXHR_PREF, originalXhr); + Services.prefs.setBoolPref(MIXED_AC_PREF, originalMixedActive); + Services.prefs.clearUserPref(PREF); + hud = null; + }); + + yield loadTab(TEST_URI); + hud = yield openConsole(); + + yield testPageLoad(); + yield testXhrGet(); + yield testXhrWarn(); + yield testXhrPost(); + yield testFormSubmission(); + yield testLiveFilteringOnSearchStrings(); +}); + +function testPageLoad() { + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_NETWORK_REQUEST_URI); + let lastRequest = yield waitForFinishedRequest(PAGE_REQUEST_PREDICATE); + + // Check if page load was logged correctly. + ok(lastRequest, "Page load was logged"); + is(lastRequest.request.url, TEST_NETWORK_REQUEST_URI, + "Logged network entry is page load"); + is(lastRequest.request.method, "GET", "Method is correct"); +} + +function testXhrGet() { + // Start the XMLHttpRequest() GET test. + ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { + content.wrappedJSObject.testXhrGet(); + }); + + let lastRequest = yield waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE); + + ok(lastRequest, "testXhrGet() was logged"); + is(lastRequest.request.method, "GET", "Method is correct"); + ok(lastRequest.isXHR, "It's an XHR request"); +} + +function testXhrWarn() { + // Start the XMLHttpRequest() warn test. + ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { + content.wrappedJSObject.testXhrWarn(); + }); + + let lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE); + if (lastRequest.request.method == "HEAD") { + // in non-e10s, we get the HEAD request that priming sends, so make sure + // a priming request should be sent, and then get the actual request + is(Services.prefs.getBoolPref("security.mixed_content.send_hsts_priming"), + true, "Found HSTS Priming Request"); + lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE); + } + + ok(lastRequest, "testXhrWarn() was logged"); + is(lastRequest.request.method, "GET", "Method is correct"); + ok(lastRequest.isXHR, "It's an XHR request"); + is(lastRequest.securityInfo, "insecure", "It's an insecure request"); +} + +function testXhrPost() { + // Start the XMLHttpRequest() POST test. + ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { + content.wrappedJSObject.testXhrPost(); + }); + + let lastRequest = yield waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE); + + ok(lastRequest, "testXhrPost() was logged"); + is(lastRequest.request.method, "POST", "Method is correct"); + ok(lastRequest.isXHR, "It's an XHR request"); +} + +function testFormSubmission() { + // Start the form submission test. As the form is submitted, the page is + // loaded again. Bind to the load event to catch when this is done. + ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() { + let form = content.document.querySelector("form"); + ok(form, "we have the HTML form"); + form.submit(); + }); + + // The form POSTs to the page URL but over https (page over http). + let lastRequest = yield waitForFinishedRequest(PAGE_REQUEST_PREDICATE); + + ok(lastRequest, "testFormSubmission() was logged"); + is(lastRequest.request.method, "POST", "Method is correct"); + + // There should be 3 network requests pointing to the HTML file. + waitForMessages({ + webconsole: hud, + messages: [ + { + text: "test-network-request.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + count: 3, + }, + { + text: "test-data.json", + category: CATEGORY_NETWORK, + severity: SEVERITY_INFO, + isXhr: true, + count: 2, + }, + { + text: "http://example.com/", + category: CATEGORY_NETWORK, + severity: SEVERITY_WARNING, + isXhr: true, + count: 1, + }, + ], + }); +} + +function testLiveFilteringOnSearchStrings() { + setStringFilter("http"); + isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " + + "search string is set to \"http\""); + + setStringFilter("HTTP"); + isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " + + "search string is set to \"HTTP\""); + + setStringFilter("hxxp"); + is(countMessageNodes(), 0, "the log nodes are hidden when the search " + + "string is set to \"hxxp\""); + + setStringFilter("ht tp"); + isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " + + "search string is set to \"ht tp\""); + + setStringFilter(""); + isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " + + "search string is removed"); + + setStringFilter("json"); + is(countMessageNodes(), 2, "the log nodes show only the nodes with \"json\""); + + setStringFilter("'foo'"); + is(countMessageNodes(), 0, "the log nodes are hidden when searching for " + + "the string 'foo'"); + + setStringFilter("foo\"bar'baz\"boo'"); + is(countMessageNodes(), 0, "the log nodes are hidden when searching for " + + "the string \"foo\"bar'baz\"boo'\""); +} + +function countMessageNodes() { + let messageNodes = hud.outputNode.querySelectorAll(".message"); + let displayedMessageNodes = 0; + let view = hud.iframeWindow; + for (let i = 0; i < messageNodes.length; i++) { + let computedStyle = view.getComputedStyle(messageNodes[i], null); + if (computedStyle.display !== "none") { + displayedMessageNodes++; + } + } + + return displayedMessageNodes; +} + +function setStringFilter(value) { + hud.ui.filterBox.value = value; + hud.ui.adjustVisibilityOnSearchStringChange(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js b/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js new file mode 100644 index 000000000..caaa73628 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console limits the number of lines displayed according to +// the user's preferences. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642108."; +const LOG_LIMIT = 20; + +function test() { + let hud; + + Task.spawn(runner).then(finishTest); + + function* runner() { + let {tab} = yield loadTab(TEST_URI); + + Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", LOG_LIMIT); + Services.prefs.setBoolPref("devtools.webconsole.filter.cssparser", true); + + registerCleanupFunction(function () { + Services.prefs.clearUserPref("devtools.hud.loglimit.cssparser"); + Services.prefs.clearUserPref("devtools.webconsole.filter.cssparser"); + }); + + hud = yield openConsole(tab); + + for (let i = 0; i < 5; i++) { + logCSSMessage("css log x"); + } + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "css log x", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + repeats: 5, + }], + }); + + for (let i = 0; i < LOG_LIMIT + 5; i++) { + logCSSMessage("css log " + i); + } + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "css log 5", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }, + { + // LOG_LIMIT + 5 + text: "css log 24", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }], + }); + + is(hud.ui.outputNode.querySelectorAll(".message").length, LOG_LIMIT, + "number of messages"); + + is(Object.keys(hud.ui._repeatNodes).length, LOG_LIMIT, + "repeated nodes pruned from repeatNodes"); + + let msg = [...result.matched][0]; + let repeats = msg.querySelector(".message-repeats"); + is(repeats.getAttribute("value"), 1, + "repeated nodes pruned from repeatNodes (confirmed)"); + } + + function logCSSMessage(msg) { + let node = hud.ui.createMessageNode(CATEGORY_CSS, SEVERITY_WARNING, msg); + hud.ui.outputMessage(CATEGORY_CSS, node); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js b/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js new file mode 100644 index 000000000..93063e436 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js @@ -0,0 +1,235 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console limits the number of lines displayed according to +// the limit set for each category. + +"use strict"; + +const INIT_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 644419: Console should " + + "have user-settable log limits for each message category"; + +const TEST_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-bug-644419-log-limits.html"; + +var hud, outputNode; + +add_task(function* () { + let { browser } = yield loadTab(INIT_URI); + + hud = yield openConsole(); + + hud.jsterm.clearOutput(); + outputNode = hud.outputNode; + + let loaded = loadBrowser(browser); + + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + yield loaded; + + yield testWebDevLimits(); + yield testWebDevLimits2(); + yield testJsLimits(); + yield testJsLimits2(); + + yield testNetLimits(); + yield loadImage(); + yield testCssLimits(); + yield testCssLimits2(); + + hud = outputNode = null; +}); + +function testWebDevLimits() { + Services.prefs.setIntPref("devtools.hud.loglimit.console", 10); + + // Find the sentinel entry. + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "bar is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }); +} + +function testWebDevLimits2() { + // Fill the log with Web Developer errors. + for (let i = 0; i < 11; i++) { + content.console.log("test message " + i); + } + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 10", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + testLogEntry(outputNode, "test message 0", "first message is pruned", + false, true); + findLogEntry("test message 1"); + // Check if the sentinel entry is still there. + findLogEntry("bar is not defined"); + + Services.prefs.clearUserPref("devtools.hud.loglimit.console"); + }); +} + +function testJsLimits() { + Services.prefs.setIntPref("devtools.hud.loglimit.exception", 10); + + hud.jsterm.clearOutput(); + content.console.log("testing JS limits"); + + // Find the sentinel entry. + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "testing JS limits", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} + +function testJsLimits2() { + // Fill the log with JS errors. + let head = content.document.getElementsByTagName("head")[0]; + for (let i = 0; i < 11; i++) { + let script = content.document.createElement("script"); + script.text = "fubar" + i + ".bogus(6);"; + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + head.insertBefore(script, head.firstChild); + } + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "fubar10 is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }], + }).then(() => { + testLogEntry(outputNode, "fubar0 is not defined", "first message is pruned", + false, true); + findLogEntry("fubar1 is not defined"); + // Check if the sentinel entry is still there. + findLogEntry("testing JS limits"); + + Services.prefs.clearUserPref("devtools.hud.loglimit.exception"); + }); +} + +var gCounter, gImage; + +function testNetLimits() { + Services.prefs.setIntPref("devtools.hud.loglimit.network", 10); + + hud.jsterm.clearOutput(); + content.console.log("testing Net limits"); + + // Find the sentinel entry. + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "testing Net limits", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + // Fill the log with network messages. + gCounter = 0; + }); +} + +function loadImage() { + if (gCounter < 11) { + let body = content.document.getElementsByTagName("body")[0]; + gImage && gImage.removeEventListener("load", loadImage, true); + gImage = content.document.createElement("img"); + gImage.src = "test-image.png?_fubar=" + gCounter; + body.insertBefore(gImage, body.firstChild); + gImage.addEventListener("load", loadImage, true); + gCounter++; + return true; + } + + is(gCounter, 11, "loaded 11 files"); + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "test-image.png", + url: "test-image.png?_fubar=10", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }).then(() => { + let msgs = outputNode.querySelectorAll(".message[category=network]"); + is(msgs.length, 10, "number of network messages"); + isnot(msgs[0].url.indexOf("fubar=1"), -1, "first network message"); + isnot(msgs[1].url.indexOf("fubar=2"), -1, "second network message"); + findLogEntry("testing Net limits"); + + Services.prefs.clearUserPref("devtools.hud.loglimit.network"); + }); +} + +function testCssLimits() { + Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", 10); + + hud.jsterm.clearOutput(); + content.console.log("testing CSS limits"); + + // Find the sentinel entry. + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "testing CSS limits", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} + +function testCssLimits2() { + // Fill the log with CSS errors. + let body = content.document.getElementsByTagName("body")[0]; + for (let i = 0; i < 11; i++) { + let div = content.document.createElement("div"); + div.setAttribute("style", "-moz-foobar" + i + ": 42;"); + body.insertBefore(div, body.firstChild); + } + + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "-moz-foobar10", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }], + }).then(() => { + testLogEntry(outputNode, "Unknown property \u2018-moz-foobar0\u2019", + "first message is pruned", false, true); + findLogEntry("Unknown property \u2018-moz-foobar1\u2019"); + // Check if the sentinel entry is still there. + findLogEntry("testing CSS limits"); + + Services.prefs.clearUserPref("devtools.hud.loglimit.cssparser"); + }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js b/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js new file mode 100644 index 000000000..81573e56f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that console logging methods display the method location along with +// the output in the console. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console file location " + + "display test"; +const TEST_URI2 = "http://example.com/browser/devtools/client/" + + "webconsole/test/" + + "test-bug-646025-console-file-location.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "message for level log", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + source: { url: "test-file-location.js", line: 8 }, + }, + { + text: "message for level info", + category: CATEGORY_WEBDEV, + severity: SEVERITY_INFO, + source: { url: "test-file-location.js", line: 9 }, + }, + { + text: "message for level warn", + category: CATEGORY_WEBDEV, + severity: SEVERITY_WARNING, + source: { url: "test-file-location.js", line: 10 }, + }, + { + text: "message for level error", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + source: { url: "test-file-location.js", line: 11 }, + }, + { + text: "message for level debug", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + source: { url: "test-file-location.js", line: 12 }, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js b/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js new file mode 100644 index 000000000..233643d51 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js @@ -0,0 +1,102 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that document.body autocompletes in the web console. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console autocompletion " + + "bug in document.body"; + +var gHUD; + +add_task(function* () { + yield loadTab(TEST_URI); + + gHUD = yield openConsole(); + + yield consoleOpened(); + yield autocompletePopupHidden(); + let view = yield testPropertyPanel(); + yield onVariablesViewReady(view); + + gHUD = null; +}); + +function consoleOpened() { + let deferred = promise.defer(); + + let jsterm = gHUD.jsterm; + let popup = jsterm.autocompletePopup; + + ok(!popup.isOpen, "popup is not open"); + + popup.once("popup-opened", () => { + ok(popup.isOpen, "popup is open"); + + is(popup.itemCount, jsterm._autocompleteCache.length, + "popup.itemCount is correct"); + isnot(jsterm._autocompleteCache.indexOf("addEventListener"), -1, + "addEventListener is in the list of suggestions"); + isnot(jsterm._autocompleteCache.indexOf("bgColor"), -1, + "bgColor is in the list of suggestions"); + isnot(jsterm._autocompleteCache.indexOf("ATTRIBUTE_NODE"), -1, + "ATTRIBUTE_NODE is in the list of suggestions"); + + popup.once("popup-closed", () => { + deferred.resolve(); + }); + EventUtils.synthesizeKey("VK_ESCAPE", {}); + }); + + jsterm.setInputValue("document.body"); + EventUtils.synthesizeKey(".", {}); + + return deferred.promise; +} + +function autocompletePopupHidden() { + let deferred = promise.defer(); + + let jsterm = gHUD.jsterm; + let popup = jsterm.autocompletePopup; + let completeNode = jsterm.completeNode; + + ok(!popup.isOpen, "popup is not open"); + + jsterm.once("autocomplete-updated", function () { + is(completeNode.value, testStr + "dy", "autocomplete shows document.body"); + deferred.resolve(); + }); + + let inputStr = "document.b"; + jsterm.setInputValue(inputStr); + EventUtils.synthesizeKey("o", {}); + let testStr = inputStr.replace(/./g, " ") + " "; + + return deferred.promise; +} + +function testPropertyPanel() { + let deferred = promise.defer(); + + let jsterm = gHUD.jsterm; + jsterm.clearOutput(); + jsterm.execute("document", (msg) => { + jsterm.once("variablesview-fetched", (evt, view) => { + deferred.resolve(view); + }); + let anchor = msg.querySelector(".message-body a"); + EventUtils.synthesizeMouse(anchor, 2, 2, {}, gHUD.iframeWindow); + }); + + return deferred.promise; +} + +function onVariablesViewReady(view) { + return findVariableViewProperties(view, [ + { name: "body", value: "<body>" }, + ], { webconsole: gHUD }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js b/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js new file mode 100644 index 000000000..217d481e2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js @@ -0,0 +1,109 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the $0 console helper works as intended. + +"use strict"; + +var inspector, h1, outputNode; + +function createDocument() { + let doc = content.document; + let div = doc.createElement("div"); + h1 = doc.createElement("h1"); + let p1 = doc.createElement("p"); + let p2 = doc.createElement("p"); + let div2 = doc.createElement("div"); + let p3 = doc.createElement("p"); + doc.title = "Inspector Tree Selection Test"; + h1.textContent = "Inspector Tree Selection Test"; + p1.textContent = "This is some example text"; + p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " + + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " + + "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " + + "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + + "dolor in reprehenderit in voluptate velit esse cillum dolore eu " + + "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " + + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " + + "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " + + "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + + "dolor in reprehenderit in voluptate velit esse cillum dolore eu " + + "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + div.appendChild(h1); + div.appendChild(p1); + div.appendChild(p2); + div2.appendChild(p3); + doc.body.appendChild(div); + doc.body.appendChild(div2); + setupHighlighterTests(); +} + +function setupHighlighterTests() { + ok(h1, "we have the header node"); + openInspector().then(runSelectionTests); +} + +var runSelectionTests = Task.async(function* (aInspector) { + inspector = aInspector; + + let onPickerStarted = inspector.toolbox.once("picker-started"); + inspector.toolbox.highlighterUtils.startPicker(); + yield onPickerStarted; + + info("Picker mode started, now clicking on H1 to select that node"); + h1.scrollIntoView(); + let onPickerStopped = inspector.toolbox.once("picker-stopped"); + let onInspectorUpdated = inspector.once("inspector-updated"); + EventUtils.synthesizeMouseAtCenter(h1, {}, content); + yield onPickerStopped; + yield onInspectorUpdated; + + info("Picker mode stopped, H1 selected, now switching to the console"); + let hud = yield openConsole(gBrowser.selectedTab); + + performWebConsoleTests(hud); +}); + +function performWebConsoleTests(hud) { + let jsterm = hud.jsterm; + outputNode = hud.outputNode; + + jsterm.clearOutput(); + jsterm.execute("$0", onNodeOutput); + + function onNodeOutput(node) { + isnot(node.textContent.indexOf("<h1>"), -1, "correct output for $0"); + + jsterm.clearOutput(); + jsterm.execute("$0.textContent = 'bug653531'", onNodeUpdate); + } + + function onNodeUpdate(node) { + isnot(node.textContent.indexOf("bug653531"), -1, + "correct output for $0.textContent"); + is(inspector.selection.node.textContent, "bug653531", + "node successfully updated"); + + inspector = h1 = outputNode = null; + gBrowser.removeCurrentTab(); + finishTest(); + } +} + +function test() { + waitForExplicitFinish(); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.selectedBrowser.addEventListener("load", function onLoad() { + gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); + waitForFocus(createDocument, content); + }, true); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, + "data:text/html;charset=utf-8,test for highlighter helper in web console"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js b/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js new file mode 100644 index 000000000..2c6c933fc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Console API implements the time() and timeEnd() methods. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-658368-time-methods.html"; + +const TEST_URI2 = "data:text/html;charset=utf-8,<script>" + + "console.timeEnd('bTimer');</script>"; + +const TEST_URI3 = "data:text/html;charset=utf-8,<script>" + + "console.time('bTimer');</script>"; + +const TEST_URI4 = "data:text/html;charset=utf-8," + + "<script>console.timeEnd('bTimer');</script>"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud1 = yield openConsole(); + + yield waitForMessages({ + webconsole: hud1, + messages: [{ + name: "aTimer started", + consoleTime: "aTimer", + }, { + name: "aTimer end", + consoleTimeEnd: "aTimer", + }], + }); + + // The next test makes sure that timers with the same name but in separate + // tabs, do not contain the same value. + let { browser } = yield loadTab(TEST_URI2); + let hud2 = yield openConsole(); + + testLogEntry(hud2.outputNode, "bTimer: timer started", + "bTimer was not started", false, true); + + // The next test makes sure that timers with the same name but in separate + // pages, do not contain the same value. + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI3); + + yield waitForMessages({ + webconsole: hud2, + messages: [{ + name: "bTimer started", + consoleTime: "bTimer", + }], + }); + + hud2.jsterm.clearOutput(); + + // Now the following console.timeEnd() call shouldn't display anything, + // if the timers in different pages are not related. + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI4); + yield loadBrowser(browser); + + testLogEntry(hud2.outputNode, "bTimer: timer started", + "bTimer was not started", false, true); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js b/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js new file mode 100644 index 000000000..03741a249 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js @@ -0,0 +1,36 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that console.dir works as intended. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 659907: Expand console object with a dir method"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + hud.jsterm.execute("console.dir(document)"); + + let varView = yield hud.jsterm.once("variablesview-fetched"); + + yield findVariableViewProperties(varView, [ + { + name: "__proto__.__proto__.querySelectorAll", + value: "querySelectorAll()" + }, + { + name: "location", + value: /Location \u2192 data:Web/ + }, + { + name: "__proto__.write", + value: "write()" + }, + ], { webconsole: hud }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js b/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js new file mode 100644 index 000000000..5906d62d6 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>bug 660806 - history " + + "navigation must not show the autocomplete popup"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield consoleOpened(hud); +}); + +function consoleOpened(HUD) { + let deferred = promise.defer(); + + let jsterm = HUD.jsterm; + let popup = jsterm.autocompletePopup; + let onShown = function () { + ok(false, "popup shown"); + }; + + jsterm.execute(`window.foobarBug660806 = { + 'location': 'value0', + 'locationbar': 'value1' + }`); + + popup.on("popup-opened", onShown); + + ok(!popup.isOpen, "popup is not open"); + + ok(!jsterm.lastInputValue, "no lastInputValue"); + jsterm.setInputValue("window.foobarBug660806.location"); + is(jsterm.lastInputValue, "window.foobarBug660806.location", + "lastInputValue is correct"); + + EventUtils.synthesizeKey("VK_RETURN", {}); + EventUtils.synthesizeKey("VK_UP", {}); + + is(jsterm.lastInputValue, "window.foobarBug660806.location", + "lastInputValue is correct, again"); + + executeSoon(function () { + ok(!popup.isOpen, "popup is not open"); + popup.off("popup-opened", onShown); + executeSoon(deferred.resolve); + }); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js b/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js new file mode 100644 index 000000000..fd510240e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js @@ -0,0 +1,79 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that console.group/groupEnd works as intended. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 664131: Expand console object with group methods"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + let jsterm = hud.jsterm; + + hud.jsterm.clearOutput(); + + yield jsterm.execute("console.group('bug664131a')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug664131a", + consoleGroup: 1, + }], + }); + + yield jsterm.execute("console.log('bug664131a-inside')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug664131a-inside", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + groupDepth: 1, + }], + }); + + yield jsterm.execute('console.groupEnd("bug664131a")'); + yield jsterm.execute('console.log("bug664131-outside")'); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug664131-outside", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + groupDepth: 0, + }], + }); + + yield jsterm.execute('console.groupCollapsed("bug664131b")'); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug664131b", + consoleGroup: 1, + }], + }); + + // Test that clearing the console removes the indentation. + hud.jsterm.clearOutput(); + yield jsterm.execute('console.log("bug664131-cleared")'); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "bug664131-cleared", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + groupDepth: 0, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js b/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js new file mode 100644 index 000000000..2b1588ef9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js @@ -0,0 +1,75 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the autocompletion results contain the names of JSTerm helpers. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test JSTerm Helpers " + + "autocomplete"; + +var jsterm; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + jsterm = hud.jsterm; + let input = jsterm.inputNode; + let popup = jsterm.autocompletePopup; + + // Test if 'i' gives 'inspect' + input.value = "i"; + input.setSelectionRange(1, 1); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + let newItems = popup.getItems().map(function (e) { + return e.label; + }); + ok(newItems.indexOf("inspect") > -1, + "autocomplete results contain helper 'inspect'"); + + // Test if 'window.' does not give 'inspect'. + input.value = "window."; + input.setSelectionRange(7, 7); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems().map(function (e) { + return e.label; + }); + is(newItems.indexOf("inspect"), -1, + "autocomplete results do not contain helper 'inspect'"); + + // Test if 'dump(i' gives 'inspect' + input.value = "dump(i"; + input.setSelectionRange(6, 6); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems().map(function (e) { + return e.label; + }); + ok(newItems.indexOf("inspect") > -1, + "autocomplete results contain helper 'inspect'"); + + // Test if 'window.dump(i' gives 'inspect' + input.value = "window.dump(i"; + input.setSelectionRange(13, 13); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems().map(function (e) { + return e.label; + }); + ok(newItems.indexOf("inspect") > -1, + "autocomplete results contain helper 'inspect'"); + + jsterm = null; +}); + +function complete(type) { + let updated = jsterm.once("autocomplete-updated"); + jsterm.complete(type); + return updated; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_704295.js b/devtools/client/webconsole/test/browser_webconsole_bug_704295.js new file mode 100644 index 000000000..df21232cf --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_704295.js @@ -0,0 +1,41 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests for bug 704295 + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + testCompletion(hud); +}); + +function testCompletion(hud) { + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + + // Test typing 'var d = 5;' and press RETURN + jsterm.setInputValue("var d = "); + EventUtils.synthesizeKey("5", {}); + EventUtils.synthesizeKey(";", {}); + is(input.value, "var d = 5;", "var d = 5;"); + is(jsterm.completeNode.value, "", "no completion"); + EventUtils.synthesizeKey("VK_RETURN", {}); + is(jsterm.completeNode.value, "", "clear completion on execute()"); + + // Test typing 'var a = d' and press RETURN + jsterm.setInputValue("var a = "); + EventUtils.synthesizeKey("d", {}); + is(input.value, "var a = d", "var a = d"); + is(jsterm.completeNode.value, "", "no completion"); + EventUtils.synthesizeKey("VK_RETURN", {}); + is(jsterm.completeNode.value, "", "clear completion on execute()"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js b/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js new file mode 100644 index 000000000..af9e172c8 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/browser/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + + is(input.getAttribute("focused"), "true", "input has focus"); + EventUtils.synthesizeKey("VK_TAB", {}); + is(input.getAttribute("focused"), "", "focus moved away"); + + // Test user changed something + input.focus(); + EventUtils.synthesizeKey("A", {}); + EventUtils.synthesizeKey("VK_TAB", {}); + is(input.getAttribute("focused"), "true", "input is still focused"); + + // Test non empty input but not changed since last focus + input.blur(); + input.focus(); + EventUtils.synthesizeKey("VK_RIGHT", {}); + EventUtils.synthesizeKey("VK_TAB", {}); + is(input.getAttribute("focused"), "", "input moved away"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js new file mode 100644 index 000000000..4665af42a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console Mixed Content messages are displayed + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Web Console mixed content test"; +const TEST_HTTPS_URI = "https://example.com/browser/devtools/client/" + + "webconsole/test/test-bug-737873-mixedcontent.html"; +const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + + "Mixed_content"; + +registerCleanupFunction(function*() { + Services.prefs.clearUserPref("security.mixed_content.block_display_content"); + Services.prefs.clearUserPref("security.mixed_content.block_active_content"); +}); + +add_task(function* () { + Services.prefs.setBoolPref("security.mixed_content.block_display_content", + false); + Services.prefs.setBoolPref("security.mixed_content.block_active_content", + false); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield testMixedContent(hud); +}); + +var testMixedContent = Task.async(function* (hud) { + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_HTTPS_URI); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "example.com", + category: CATEGORY_NETWORK, + severity: SEVERITY_WARNING, + }], + }); + + let msg = [...results[0].matched][0]; + ok(msg, "page load logged"); + ok(msg.classList.contains("mixed-content"), ".mixed-content element"); + + let link = msg.querySelector(".learn-more-link"); + ok(link, "mixed content link element"); + is(link.textContent, "[Mixed Content]", "link text is accurate"); + + yield simulateMessageLinkClick(link, LEARN_MORE_URI); + + ok(!msg.classList.contains("filtered-by-type"), "message is not filtered"); + + hud.setFilterState("netwarn", false); + + ok(msg.classList.contains("filtered-by-type"), "message is filtered"); + + hud.setFilterState("netwarn", true); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js new file mode 100644 index 000000000..85b99a79a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js @@ -0,0 +1,83 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that warnings about ineffective iframe sandboxing are logged to the +// web console when necessary (and not otherwise). + +"use strict"; + +requestLongerTimeout(2); + +const TEST_URI_WARNING = "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html"; +const TEST_URI_NOWARNING = [ + "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html", + "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html", + "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html", + "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html", + "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html" +]; + +const INEFFECTIVE_IFRAME_SANDBOXING_MSG = "An iframe which has both " + + "allow-scripts and allow-same-origin for its sandbox attribute can remove " + + "its sandboxing."; +const SENTINEL_MSG = "testing ineffective sandboxing message"; + +add_task(function* () { + yield testYesWarning(); + + for (let id = 0; id < TEST_URI_NOWARNING.length; id++) { + yield testNoWarning(id); + } +}); + +function* testYesWarning() { + yield loadTab(TEST_URI_WARNING); + let hud = yield openConsole(); + + ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) { + content.console.log(msg); + }); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Ineffective iframe sandboxing warning displayed successfully", + text: INEFFECTIVE_IFRAME_SANDBOXING_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + { + text: SENTINEL_MSG, + severity: SEVERITY_LOG + } + ] + }); + + let msgs = hud.outputNode.querySelectorAll(".message[category=security]"); + is(msgs.length, 1, "one security message"); +} + +function* testNoWarning(id) { + yield loadTab(TEST_URI_NOWARNING[id]); + let hud = yield openConsole(); + + ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) { + content.console.log(msg); + }); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: SENTINEL_MSG, + severity: SEVERITY_LOG + } + ] + }); + + let msgs = hud.outputNode.querySelectorAll(".message[category=security]"); + is(msgs.length, 0, "no security messages (case " + id + ")"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js new file mode 100644 index 000000000..49df4d1fc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that errors about insecure passwords are logged to the web console. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html"; +const INSECURE_PASSWORD_MSG = "Password fields present on an insecure " + + "(http://) iframe. This is a security risk that allows user login " + + "credentials to be stolen."; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Insecure password error displayed successfully", + text: INSECURE_PASSWORD_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + ], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js new file mode 100644 index 000000000..00a620fc8 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js @@ -0,0 +1,62 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + // Tests that errors about insecure passwords are logged to the web console. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-762593-insecure-passwords-web-" + + "console-warning.html"; +const INSECURE_PASSWORD_MSG = "Password fields present on an insecure " + + "(http://) page. This is a security risk that allows user " + + "login credentials to be stolen."; +const INSECURE_FORM_ACTION_MSG = "Password fields present in a form with an " + + "insecure (http://) form action. This is a security risk " + + "that allows user login credentials to be stolen."; +const INSECURE_IFRAME_MSG = "Password fields present on an insecure " + + "(http://) iframe. This is a security risk that allows " + + "user login credentials to be stolen."; +const INSECURE_PASSWORDS_URI = "https://developer.mozilla.org/docs/Web/" + + "Security/Insecure_passwords" + DOCS_GA_PARAMS; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let result = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Insecure password error displayed successfully", + text: INSECURE_PASSWORD_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + { + name: "Insecure iframe error displayed successfully", + text: INSECURE_IFRAME_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + { + name: "Insecure form action error displayed successfully", + text: INSECURE_FORM_ACTION_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + ], + }); + + yield testClickOpenNewTab(hud, result); +}); + +function testClickOpenNewTab(hud, [result]) { + let msg = [...result.matched][0]; + let warningNode = msg.querySelector(".learn-more-link"); + ok(warningNode, "learn more link"); + return simulateMessageLinkClick(warningNode, INSECURE_PASSWORDS_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js b/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js new file mode 100644 index 000000000..731e79d8b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js @@ -0,0 +1,142 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// This is a test for the Open URL context menu item +// that is shown for network requests + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; +const COMMAND_NAME = "consoleCmd_openURL"; +const CONTEXT_MENU_ID = "#menu_openURL"; + +var HUD = null, outputNode = null, contextMenu = null; + +add_task(function* () { + Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true); + + yield loadTab(TEST_URI); + HUD = yield openConsole(); + + let results = yield consoleOpened(); + yield onConsoleMessage(results); + + let results2 = yield testOnNetActivity(); + let msg = yield onNetworkMessage(results2); + + yield testOnNetActivityContextMenu(msg); + + Services.prefs.clearUserPref("devtools.webconsole.filter.networkinfo"); + + HUD = null; + outputNode = null; + contextMenu = null; +}); + +function consoleOpened() { + outputNode = HUD.outputNode; + contextMenu = HUD.iframeWindow.document.getElementById("output-contextmenu"); + + HUD.jsterm.clearOutput(); + + content.console.log("bug 764572"); + + return waitForMessages({ + webconsole: HUD, + messages: [{ + text: "bug 764572", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} + +function onConsoleMessage(results) { + outputNode.focus(); + outputNode.selectedItem = [...results[0].matched][0]; + + // Check if the command is disabled non-network messages. + goUpdateCommand(COMMAND_NAME); + let controller = top.document.commandDispatcher + .getControllerForCommand(COMMAND_NAME); + + let isDisabled = !controller || !controller.isCommandEnabled(COMMAND_NAME); + ok(isDisabled, COMMAND_NAME + " should be disabled."); + + return waitForContextMenu(contextMenu, outputNode.selectedItem, () => { + let isHidden = contextMenu.querySelector(CONTEXT_MENU_ID).hidden; + ok(isHidden, CONTEXT_MENU_ID + " should be hidden."); + }); +} + +function testOnNetActivity() { + HUD.jsterm.clearOutput(); + + // Reload the url to show net activity in console. + content.location.reload(); + + return waitForMessages({ + webconsole: HUD, + messages: [{ + text: "test-console.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); +} + +function onNetworkMessage(results) { + let deferred = promise.defer(); + + outputNode.focus(); + let msg = [...results[0].matched][0]; + ok(msg, "network message"); + HUD.ui.output.selectMessage(msg); + + let currentTab = gBrowser.selectedTab; + let newTab = null; + + gBrowser.tabContainer.addEventListener("TabOpen", function onOpen(evt) { + gBrowser.tabContainer.removeEventListener("TabOpen", onOpen, true); + newTab = evt.target; + newTab.linkedBrowser.addEventListener("load", onTabLoaded, true); + }, true); + + function onTabLoaded() { + newTab.linkedBrowser.removeEventListener("load", onTabLoaded, true); + gBrowser.removeTab(newTab); + gBrowser.selectedTab = currentTab; + executeSoon(deferred.resolve.bind(null, msg)); + } + + // Check if the command is enabled for a network message. + goUpdateCommand(COMMAND_NAME); + let controller = top.document.commandDispatcher + .getControllerForCommand(COMMAND_NAME); + ok(controller.isCommandEnabled(COMMAND_NAME), + COMMAND_NAME + " should be enabled."); + + // Try to open the URL. + goDoCommand(COMMAND_NAME); + + return deferred.promise; +} + +function testOnNetActivityContextMenu(msg) { + let deferred = promise.defer(); + + outputNode.focus(); + HUD.ui.output.selectMessage(msg); + + info("net activity context menu"); + + waitForContextMenu(contextMenu, msg, () => { + let isShown = !contextMenu.querySelector(CONTEXT_MENU_ID).hidden; + ok(isShown, CONTEXT_MENU_ID + " should be shown."); + }).then(deferred.resolve); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js new file mode 100644 index 000000000..3686fba89 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js @@ -0,0 +1,88 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that message source links for js errors and console API calls open in +// the jsdebugger when clicked. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test" + + "/test-bug-766001-js-console-links.html"; + +// Force the new debugger UI, in case this gets uplifted with the old +// debugger still turned on +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function test() { + let hud; + + requestLongerTimeout(2); + Task.spawn(runner).then(finishTest); + + function* runner() { + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + + let {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + let [exceptionRule, consoleRule] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "document.bar", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + { + text: "Blah Blah", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let exceptionMsg = [...exceptionRule.matched][0]; + let consoleMsg = [...consoleRule.matched][0]; + let nodes = [exceptionMsg.querySelector(".message-location > .frame-link"), + consoleMsg.querySelector(".message-location > .frame-link")]; + ok(nodes[0], ".location node for the exception message"); + ok(nodes[1], ".location node for the console message"); + + for (let i = 0; i < nodes.length; i++) { + yield checkClickOnNode(i, nodes[i]); + yield gDevTools.showToolbox(hud.target, "webconsole"); + } + + // check again the first node. + yield checkClickOnNode(0, nodes[0]); + } + + function* checkClickOnNode(index, node) { + info("checking click on node index " + index); + + let url = node.getAttribute("data-url"); + ok(url, "source url found for index " + index); + + let line = node.getAttribute("data-line"); + ok(line, "found source line for index " + index); + + executeSoon(() => { + EventUtils.sendMouseEvent({ type: "click" }, node.querySelector(".frame-link-filename")); + }); + + yield hud.ui.once("source-in-debugger-opened"); + + let toolbox = yield gDevTools.getToolbox(hud.target); + let dbg = toolbox.getPanel("jsdebugger"); + is(dbg._selectors().getSelectedSource(dbg._getState()).get("url"), + url, + "expected source url"); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js b/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js new file mode 100644 index 000000000..3a7134202 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console CSP messages are displayed + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test"; +const TEST_VIOLATION = "https://example.com/browser/devtools/client/" + + "webconsole/test/test_bug_770099_violation.html"; +const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " + + "blocked the loading of a resource at " + + "http://some.example.com/test.png (\u201cdefault-src " + + "https://example.com\u201d)."; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_VIOLATION); + yield loaded; + + yield waitForSuccess({ + name: "CSP policy URI warning displayed successfully", + validator: function () { + return hud.outputNode.textContent.indexOf(CSP_VIOLATION_MSG) > -1; + } + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js new file mode 100644 index 000000000..f2efd7922 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js @@ -0,0 +1,140 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test" + + "/test-bug-782653-css-errors.html"; + +var nodes, hud, StyleEditorUI; + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + + let styleEditor = yield testViewSource(); + yield onStyleEditorReady(styleEditor); + + nodes = hud = StyleEditorUI = null; +}); + +function testViewSource() { + let deferred = promise.defer(); + + waitForMessages({ + webconsole: hud, + messages: [{ + text: "\u2018font-weight\u2019", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }, + { + text: "\u2018color\u2019", + category: CATEGORY_CSS, + severity: SEVERITY_WARNING, + }], + }).then(([error1Rule, error2Rule]) => { + let error1Msg = [...error1Rule.matched][0]; + let error2Msg = [...error2Rule.matched][0]; + nodes = [error1Msg.querySelector(".message-location .frame-link"), + error2Msg.querySelector(".message-location .frame-link")]; + ok(nodes[0], ".frame-link node for the first error"); + ok(nodes[1], ".frame-link node for the second error"); + + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + toolbox.once("styleeditor-selected", (event, panel) => { + StyleEditorUI = panel.UI; + deferred.resolve(panel); + }); + + EventUtils.sendMouseEvent({ type: "click" }, nodes[0].querySelector(".frame-link-filename")); + }); + + return deferred.promise; +} + +function onStyleEditorReady(panel) { + let deferred = promise.defer(); + + let win = panel.panelWindow; + ok(win, "Style Editor Window is defined"); + ok(StyleEditorUI, "Style Editor UI is defined"); + + function fireEvent(toolbox, href, line) { + toolbox.once("styleeditor-selected", function (evt) { + info(evt + " event fired"); + + checkStyleEditorForSheetAndLine(href, line - 1).then(deferred.resolve); + }); + + EventUtils.sendMouseEvent({ type: "click" }, nodes[1].querySelector(".frame-link-filename")); + } + + waitForFocus(function () { + info("style editor window focused"); + + let href = nodes[0].getAttribute("data-url"); + let line = nodes[0].getAttribute("data-line"); + ok(line, "found source line"); + + checkStyleEditorForSheetAndLine(href, line - 1).then(function () { + info("first check done"); + + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + + href = nodes[1].getAttribute("data-url"); + line = nodes[1].getAttribute("data-line"); + ok(line, "found source line"); + + toolbox.selectTool("webconsole").then(function () { + info("webconsole selected"); + fireEvent(toolbox, href, line); + }); + }); + }, win); + + return deferred.promise; +} + +function checkStyleEditorForSheetAndLine(href, line) { + let foundEditor = null; + for (let editor of StyleEditorUI.editors) { + if (editor.styleSheet.href == href) { + foundEditor = editor; + break; + } + } + + ok(foundEditor, "found style editor for " + href); + return performLineCheck(foundEditor, line); +} + +function performLineCheck(editor, line) { + let deferred = promise.defer(); + + function checkForCorrectState() { + is(editor.sourceEditor.getCursor().line, line, + "correct line is selected"); + is(StyleEditorUI.selectedStyleSheetIndex, editor.styleSheet.styleSheetIndex, + "correct stylesheet is selected in the editor"); + + executeSoon(deferred.resolve); + } + + info("wait for source editor to load"); + + // Get out of the styleeditor-selected event loop. + executeSoon(() => { + editor.getSourceEditor().then(() => { + // Get out of the editor's source-editor-load event loop. + executeSoon(checkForCorrectState); + }); + }); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js b/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js new file mode 100644 index 000000000..b040e6314 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js @@ -0,0 +1,227 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test navigation of webconsole contents via ctrl-a, ctrl-e, ctrl-p, ctrl-n +// see https://bugzilla.mozilla.org/show_bug.cgi?id=804845 +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 804845 and bug 619598"; + +var jsterm, inputNode; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + doTests(hud); + + jsterm = inputNode = null; +}); + +function doTests(HUD) { + jsterm = HUD.jsterm; + inputNode = jsterm.inputNode; + ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty"); + is(jsterm.inputNode.selectionStart, 0); + is(jsterm.inputNode.selectionEnd, 0); + + testSingleLineInputNavNoHistory(); + testMultiLineInputNavNoHistory(); + testNavWithHistory(); +} + +function testSingleLineInputNavNoHistory() { + // Single char input + EventUtils.synthesizeKey("1", {}); + is(inputNode.selectionStart, 1, "caret location after single char input"); + + // nav to start/end with ctrl-a and ctrl-e; + EventUtils.synthesizeKey("a", { ctrlKey: true }); + is(inputNode.selectionStart, 0, + "caret location after single char input and ctrl-a"); + + EventUtils.synthesizeKey("e", { ctrlKey: true }); + is(inputNode.selectionStart, 1, + "caret location after single char input and ctrl-e"); + + // Second char input + EventUtils.synthesizeKey("2", {}); + // nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n + EventUtils.synthesizeKey("VK_UP", {}); + is(inputNode.selectionStart, 0, + "caret location after two char input and VK_UP"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(inputNode.selectionStart, 2, + "caret location after two char input and VK_DOWN"); + + EventUtils.synthesizeKey("a", { ctrlKey: true }); + is(inputNode.selectionStart, 0, + "move caret to beginning of 2 char input with ctrl-a"); + EventUtils.synthesizeKey("a", { ctrlKey: true }); + is(inputNode.selectionStart, 0, + "no change of caret location on repeat ctrl-a"); + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(inputNode.selectionStart, 0, + "no change of caret location on ctrl-p from beginning of line"); + + EventUtils.synthesizeKey("e", { ctrlKey: true }); + is(inputNode.selectionStart, 2, + "move caret to end of 2 char input with ctrl-e"); + EventUtils.synthesizeKey("e", { ctrlKey: true }); + is(inputNode.selectionStart, 2, + "no change of caret location on repeat ctrl-e"); + EventUtils.synthesizeKey("n", { ctrlKey: true }); + is(inputNode.selectionStart, 2, + "no change of caret location on ctrl-n from end of line"); + + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(inputNode.selectionStart, 0, "ctrl-p moves to start of line"); + + EventUtils.synthesizeKey("n", { ctrlKey: true }); + is(inputNode.selectionStart, 2, "ctrl-n moves to end of line"); +} + +function testMultiLineInputNavNoHistory() { + let lineValues = ["one", "2", "something longer", "", "", "three!"]; + jsterm.setInputValue(""); + // simulate shift-return + for (let i = 0; i < lineValues.length; i++) { + jsterm.setInputValue(jsterm.getInputValue() + lineValues[i]); + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true }); + } + let inputValue = jsterm.getInputValue(); + is(inputNode.selectionStart, inputNode.selectionEnd); + is(inputNode.selectionStart, inputValue.length, + "caret at end of multiline input"); + + // possibility newline is represented by one ('\r', '\n') or two + // ('\r\n') chars + let newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0]; + + // Ok, test navigating within the multi-line string! + EventUtils.synthesizeKey("VK_UP", {}); + let expectedStringAfterCarat = lineValues[5] + newlineString; + is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat, + "up arrow from end of multiline"); + + EventUtils.synthesizeKey("VK_DOWN", {}); + is(jsterm.getInputValue().slice(inputNode.selectionStart), "", + "down arrow from within multiline"); + + // navigate up through input lines + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat, + "ctrl-p from end of multiline"); + + for (let i = 4; i >= 0; i--) { + EventUtils.synthesizeKey("p", { ctrlKey: true }); + expectedStringAfterCarat = lineValues[i] + newlineString + + expectedStringAfterCarat; + is(jsterm.getInputValue().slice(inputNode.selectionStart), + expectedStringAfterCarat, "ctrl-p from within line " + i + + " of multiline input"); + } + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(inputNode.selectionStart, 0, "reached start of input"); + is(jsterm.getInputValue(), inputValue, + "no change to multiline input on ctrl-p from beginning of multiline"); + + // navigate to end of first line + EventUtils.synthesizeKey("e", { ctrlKey: true }); + let caretPos = inputNode.selectionStart; + let expectedStringBeforeCarat = lineValues[0]; + is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat, + "ctrl-e into multiline input"); + EventUtils.synthesizeKey("e", { ctrlKey: true }); + is(inputNode.selectionStart, caretPos, + "repeat ctrl-e doesn't change caret position in multiline input"); + + // navigate down one line; ctrl-a to the beginning; ctrl-e to end + for (let i = 1; i < lineValues.length; i++) { + EventUtils.synthesizeKey("n", { ctrlKey: true }); + EventUtils.synthesizeKey("a", { ctrlKey: true }); + caretPos = inputNode.selectionStart; + expectedStringBeforeCarat += newlineString; + is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat, + "ctrl-a to beginning of line " + (i + 1) + " in multiline input"); + + EventUtils.synthesizeKey("e", { ctrlKey: true }); + caretPos = inputNode.selectionStart; + expectedStringBeforeCarat += lineValues[i]; + is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat, + "ctrl-e to end of line " + (i + 1) + "in multiline input"); + } +} + +function testNavWithHistory() { + // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with + // caret placed _within_ single line input + let values = ['"single line input"', + '"a longer single-line input to check caret repositioning"', + ['"multi-line"', '"input"', '"here!"'].join("\n"), + ]; + // submit to history + for (let i = 0; i < values.length; i++) { + jsterm.setInputValue(values[i]); + jsterm.execute(); + } + is(inputNode.selectionStart, 0, "caret location at start of empty line"); + + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(inputNode.selectionStart, values[values.length - 1].length, + "caret location correct at end of last history input"); + + // Navigate backwards history with ctrl-p + for (let i = values.length - 1; i > 0; i--) { + let match = values[i].match(/(\n)/g); + if (match) { + // multi-line inputs won't update from history unless caret at beginning + EventUtils.synthesizeKey("a", { ctrlKey: true }); + for (let j = 0; j < match.length; j++) { + EventUtils.synthesizeKey("p", { ctrlKey: true }); + } + EventUtils.synthesizeKey("p", { ctrlKey: true }); + } else { + // single-line inputs will update from history from end of line + EventUtils.synthesizeKey("p", { ctrlKey: true }); + } + is(jsterm.getInputValue(), values[i - 1], + "ctrl-p updates inputNode from backwards history values[" + i - 1 + "]"); + } + let inputValue = jsterm.getInputValue(); + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(inputNode.selectionStart, 0, + "ctrl-p at beginning of history moves caret location to beginning " + + "of line"); + is(jsterm.getInputValue(), inputValue, + "no change to input value on ctrl-p from beginning of line"); + + // Navigate forwards history with ctrl-n + for (let i = 1; i < values.length; i++) { + EventUtils.synthesizeKey("n", { ctrlKey: true }); + is(jsterm.getInputValue(), values[i], + "ctrl-n updates inputNode from forwards history values[" + i + "]"); + is(inputNode.selectionStart, values[i].length, + "caret location correct at end of history input for values[" + i + "]"); + } + EventUtils.synthesizeKey("n", { ctrlKey: true }); + ok(!jsterm.getInputValue(), "ctrl-n at end of history updates to empty input"); + + // Simulate editing multi-line + inputValue = "one\nlinebreak"; + jsterm.setInputValue(inputValue); + + // Attempt nav within input + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(jsterm.getInputValue(), inputValue, + "ctrl-p from end of multi-line does not trigger history"); + + EventUtils.synthesizeKey("a", { ctrlKey: true }); + EventUtils.synthesizeKey("p", { ctrlKey: true }); + is(jsterm.getInputValue(), values[values.length - 1], + "ctrl-p from start of multi-line triggers history"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js b/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js new file mode 100644 index 000000000..ccbcb3bf3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that user input that is not submitted in the command line input is not +// lost after navigating in history. +// See https://bugzilla.mozilla.org/show_bug.cgi?id=817834 + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + testEditedInputHistory(hud); +}); + +function testEditedInputHistory(HUD) { + let jsterm = HUD.jsterm; + let inputNode = jsterm.inputNode; + ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty"); + is(inputNode.selectionStart, 0); + is(inputNode.selectionEnd, 0); + + jsterm.setInputValue('"first item"'); + EventUtils.synthesizeKey("VK_UP", {}); + is(jsterm.getInputValue(), '"first item"', "null test history up"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(jsterm.getInputValue(), '"first item"', "null test history down"); + + jsterm.execute(); + is(jsterm.getInputValue(), "", "cleared input line after submit"); + + jsterm.setInputValue('"editing input 1"'); + EventUtils.synthesizeKey("VK_UP", {}); + is(jsterm.getInputValue(), '"first item"', "test history up"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(jsterm.getInputValue(), '"editing input 1"', + "test history down restores in-progress input"); + + jsterm.setInputValue('"second item"'); + jsterm.execute(); + jsterm.setInputValue('"editing input 2"'); + EventUtils.synthesizeKey("VK_UP", {}); + is(jsterm.getInputValue(), '"second item"', "test history up"); + EventUtils.synthesizeKey("VK_UP", {}); + is(jsterm.getInputValue(), '"first item"', "test history up"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(jsterm.getInputValue(), '"second item"', "test history down"); + EventUtils.synthesizeKey("VK_DOWN", {}); + is(jsterm.getInputValue(), '"editing input 2"', + "test history down restores new in-progress input again"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js new file mode 100644 index 000000000..0524e1c4c --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js @@ -0,0 +1,42 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-837351-security-errors.html"; + +add_task(function* () { + yield pushPrefEnv(); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let button = hud.ui.rootElement.querySelector(".webconsole-filter-button[category=\"security\"]"); + ok(button, "Found security button in the web console"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Logged blocking mixed active content", + text: "Blocked loading mixed active content \u201chttp://example.com/\u201d", + category: CATEGORY_SECURITY, + severity: SEVERITY_ERROR + }, + ], + }); +}); + +function pushPrefEnv() { + let deferred = promise.defer(); + let options = { + set: [["security.mixed_content.block_active_content", true]] + }; + SpecialPowers.pushPrefEnv(options, deferred.resolve); + return deferred.promise; +} + diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js b/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js new file mode 100644 index 000000000..8062ffeec --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js @@ -0,0 +1,48 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that console.dirxml works as intended. + +"use strict"; + +const TEST_URI = `data:text/html;charset=utf-8,Web Console test for bug 922212: + Add console.dirxml`; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + // Should work like console.log(window) + hud.jsterm.execute("console.dirxml(window)"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.dirxml(window) output:", + text: /Window \u2192/, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + hud.jsterm.clearOutput(); + + hud.jsterm.execute("console.dirxml(document.body)"); + + // Should work like console.log(document.body); + [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.dirxml(document.body) output:", + text: "<body>", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + let msg = [...result.matched][0]; + yield checkLinkToInspector(true, msg); +}); + diff --git a/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js b/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js new file mode 100644 index 000000000..fd5c4d29a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js @@ -0,0 +1,114 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the cached autocomplete results are used when the new +// user input is a subset of the existing completion results. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test cached autocompletion " + + "results"; + +var jsterm; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + jsterm = hud.jsterm; + let input = jsterm.inputNode; + let popup = jsterm.autocompletePopup; + + // Test if 'doc' gives 'document' + input.value = "doc"; + input.setSelectionRange(3, 3); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(input.value, "doc", "'docu' completion (input.value)"); + is(jsterm.completeNode.value, " ument", "'docu' completion (completeNode)"); + + // Test typing 'window.'. + input.value = "window."; + input.setSelectionRange(7, 7); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + ok(popup.getItems().length > 0, "'window.' gave a list of suggestions"); + + yield jsterm.execute("window.docfoobar = true"); + + // Test typing 'window.doc'. + input.value = "window.doc"; + input.setSelectionRange(10, 10); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + let newItems = popup.getItems(); + ok(newItems.every(function (item) { + return item.label != "docfoobar"; + }), "autocomplete cached results do not contain docfoobar. list has not " + + "been updated"); + + // Test that backspace does not cause a request to the server + input.value = "window.do"; + input.setSelectionRange(9, 9); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems(); + ok(newItems.every(function (item) { + return item.label != "docfoobar"; + }), "autocomplete cached results do not contain docfoobar. list has not " + + "been updated"); + + yield jsterm.execute("delete window.docfoobar"); + + // Test if 'window.getC' gives 'getComputedStyle' + input.value = "window."; + input.setSelectionRange(7, 7); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + input.value = "window.getC"; + input.setSelectionRange(11, 11); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems(); + ok(!newItems.every(function (item) { + return item.label != "getComputedStyle"; + }), "autocomplete results do contain getComputedStyle"); + + // Test if 'dump(d' gives non-zero results + input.value = "dump(d"; + input.setSelectionRange(6, 6); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + ok(popup.getItems().length > 0, "'dump(d' gives non-zero results"); + + // Test that 'dump(window.)' works. + input.value = "dump(window.)"; + input.setSelectionRange(12, 12); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + yield jsterm.execute("window.docfoobar = true"); + + // Make sure 'dump(window.doc)' does not contain 'docfoobar'. + input.value = "dump(window.doc)"; + input.setSelectionRange(15, 15); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + newItems = popup.getItems(); + ok(newItems.every(function (item) { + return item.label != "docfoobar"; + }), "autocomplete cached results do not contain docfoobar. list has not " + + "been updated"); + + yield jsterm.execute("delete window.docfoobar"); + + jsterm = null; +}); + +function complete(type) { + let updated = jsterm.once("autocomplete-updated"); + jsterm.complete(type); + return updated; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js b/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js new file mode 100644 index 000000000..480c60940 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js @@ -0,0 +1,115 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the cd() jsterm helper function works as expected. See bug 609872. + +"use strict"; + +function test() { + let hud; + + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug-609872-cd-iframe-parent.html"; + + const parentMessages = [{ + name: "document.title in parent iframe", + text: "bug 609872 - iframe parent", + category: CATEGORY_OUTPUT, + }, { + name: "paragraph content", + text: "p: test for bug 609872 - iframe parent", + category: CATEGORY_OUTPUT, + }, { + name: "object content", + text: "obj: parent!", + category: CATEGORY_OUTPUT, + }]; + + const childMessages = [{ + name: "document.title in child iframe", + text: "bug 609872 - iframe child", + category: CATEGORY_OUTPUT, + }, { + name: "paragraph content", + text: "p: test for bug 609872 - iframe child", + category: CATEGORY_OUTPUT, + }, { + name: "object content", + text: "obj: child!", + category: CATEGORY_OUTPUT, + }]; + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + yield executeWindowTest(); + + yield waitForMessages({ webconsole: hud, messages: parentMessages }); + + info("cd() into the iframe using a selector"); + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd('iframe')"); + yield executeWindowTest(); + + yield waitForMessages({ webconsole: hud, messages: childMessages }); + + info("cd() out of the iframe, reset to default window"); + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd()"); + yield executeWindowTest(); + + yield waitForMessages({ webconsole: hud, messages: parentMessages }); + + info("call cd() with unexpected arguments"); + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd(document)"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Cannot cd()", + category: CATEGORY_OUTPUT, + severity: SEVERITY_ERROR, + }], + }); + + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd('p')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Cannot cd()", + category: CATEGORY_OUTPUT, + severity: SEVERITY_ERROR, + }], + }); + + info("cd() into the iframe using an iframe DOM element"); + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd($('iframe'))"); + yield executeWindowTest(); + + yield waitForMessages({ webconsole: hud, messages: childMessages }); + + info("cd(window.parent)"); + hud.jsterm.clearOutput(); + yield hud.jsterm.execute("cd(window.parent)"); + yield executeWindowTest(); + + yield waitForMessages({ webconsole: hud, messages: parentMessages }); + + yield closeConsole(tab); + } + + function* executeWindowTest() { + yield hud.jsterm.execute("document.title"); + yield hud.jsterm.execute("'p: ' + document.querySelector('p').textContent"); + yield hud.jsterm.execute("'obj: ' + window.foobarBug609872"); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js b/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js new file mode 100644 index 000000000..ca08d1a0f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the Web Console shows weak crypto warnings (SHA-1 Certificate) + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Web Console weak crypto " + + "warnings test"; +const TEST_URI_PATH = "/browser/devtools/client/webconsole/test/" + + "test-certificate-messages.html"; + +var gWebconsoleTests = [ + {url: "https://sha1ee.example.com" + TEST_URI_PATH, + name: "SHA1 warning displayed successfully", + warning: ["SHA-1"], nowarning: ["SSL 3.0", "RC4"]}, + {url: "https://sha256ee.example.com" + TEST_URI_PATH, + name: "SSL warnings appropriately not present", + warning: [], nowarning: ["SHA-1", "SSL 3.0", "RC4"]}, +]; +const TRIGGER_MSG = "If you haven't seen ssl warnings yet, you won't"; + +var gHud = undefined, gContentBrowser; +var gCurrentTest; + +function test() { + registerCleanupFunction(function () { + gHud = gContentBrowser = null; + }); + + loadTab(TEST_URI).then(({browser}) => { + gContentBrowser = browser; + openConsole().then(runTestLoop); + }); +} + +function runTestLoop(theHud) { + gCurrentTest = gWebconsoleTests.shift(); + if (!gCurrentTest) { + finishTest(); + return; + } + if (!gHud) { + gHud = theHud; + } + gHud.jsterm.clearOutput(); + gContentBrowser.addEventListener("load", onLoad, true); + if (gCurrentTest.pref) { + SpecialPowers.pushPrefEnv({"set": gCurrentTest.pref}, + function () { + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url); + }); + } else { + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url); + } +} + +function onLoad() { + gContentBrowser.removeEventListener("load", onLoad, true); + + waitForSuccess({ + name: gCurrentTest.name, + validator: function () { + if (gHud.outputNode.textContent.indexOf(TRIGGER_MSG) >= 0) { + for (let warning of gCurrentTest.warning) { + if (gHud.outputNode.textContent.indexOf(warning) < 0) { + return false; + } + } + for (let nowarning of gCurrentTest.nowarning) { + if (gHud.outputNode.textContent.indexOf(nowarning) >= 0) { + return false; + } + } + return true; + } + } + }).then(runTestLoop); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_chrome.js b/devtools/client/webconsole/test/browser_webconsole_chrome.js new file mode 100644 index 000000000..2513d1df5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_chrome.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that code completion works properly in chrome tabs, like about:credits. + +"use strict"; + +function test() { + Task.spawn(function* () { + const {tab} = yield loadTab("about:config"); + ok(tab, "tab loaded"); + + const hud = yield openConsole(tab); + ok(hud, "we have a console"); + ok(hud.iframeWindow, "we have the console UI window"); + + let jsterm = hud.jsterm; + ok(jsterm, "we have a jsterm"); + + let input = jsterm.inputNode; + ok(hud.outputNode, "we have an output node"); + + // Test typing 'docu'. + input.value = "docu"; + input.setSelectionRange(4, 4); + + let deferred = promise.defer(); + + jsterm.complete(jsterm.COMPLETE_HINT_ONLY, function () { + is(jsterm.completeNode.value, " ment", "'docu' completion"); + deferred.resolve(null); + }); + + yield deferred.promise; + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_clear_method.js b/devtools/client/webconsole/test/browser_webconsole_clear_method.js new file mode 100644 index 000000000..a4702980e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_clear_method.js @@ -0,0 +1,131 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that calls to console.clear from a script delete the messages +// previously logged. + +"use strict"; + +add_task(function* () { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-clear.html"; + + yield loadTab(TEST_URI); + let hud = yield openConsole(); + ok(hud, "Web Console opened"); + + info("Check the console.clear() done on page load has been processed."); + yield waitForLog("Console was cleared", hud); + ok(hud.outputNode.textContent.includes("Console was cleared"), + "console.clear() message is displayed"); + ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed"); + ok(!hud.outputNode.textContent.includes("log2"), "log2 not displayed"); + + info("Logging two messages log3, log4"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.console.log("log3"); + content.wrappedJSObject.console.log("log4"); + }); + + yield waitForLog("log3", hud); + yield waitForLog("log4", hud); + + ok(hud.outputNode.textContent.includes("Console was cleared"), + "console.clear() message is still displayed"); + ok(hud.outputNode.textContent.includes("log3"), "log3 is displayed"); + ok(hud.outputNode.textContent.includes("log4"), "log4 is displayed"); + + info("Open the variables view sidebar for 'objFromPage'"); + yield openSidebar("objFromPage", { a: 1 }, hud); + let sidebarClosed = hud.jsterm.once("sidebar-closed"); + + info("Call console.clear from the page"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.console.clear(); + }); + + // Cannot wait for "Console was cleared" here because such a message is + // already present and would yield immediately. + info("Wait for variables view sidebar to be closed after console.clear()"); + yield sidebarClosed; + + ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed"); + ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed"); + ok(hud.outputNode.textContent.includes("Console was cleared"), + "console.clear() message is still displayed"); + is(hud.outputNode.textContent.split("Console was cleared").length, 2, + "console.clear() message is only displayed once"); + + info("Logging one messages log5"); + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + content.wrappedJSObject.console.log("log5"); + }); + yield waitForLog("log5", hud); + + info("Close and reopen the webconsole."); + yield closeConsole(gBrowser.selectedTab); + hud = yield openConsole(); + yield waitForLog("Console was cleared", hud); + + ok(hud.outputNode.textContent.includes("Console was cleared"), + "console.clear() message is still displayed"); + ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed"); + ok(!hud.outputNode.textContent.includes("log2"), "log1 not displayed"); + ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed"); + ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed"); + ok(hud.outputNode.textContent.includes("log5"), "log5 still displayed"); +}); + +/** + * Wait for a single message to be logged in the provided webconsole instance + * with the category CATEGORY_WEBDEV and the SEVERITY_LOG severity. + * + * @param {String} message + * The expected messaged. + * @param {WebConsole} webconsole + * WebConsole instance in which the message should be logged. + */ +function* waitForLog(message, webconsole, options) { + yield waitForMessages({ + webconsole: webconsole, + messages: [{ + text: message, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +} + +/** + * Open the variables view sidebar for the object with the provided name objName + * and wait for the expected object is displayed in the variables view. + * + * @param {String} objName + * The name of the object to open in the sidebar. + * @param {Object} expectedObj + * The properties that should be displayed in the variables view. + * @param {WebConsole} webconsole + * WebConsole instance in which the message should be logged. + * + */ +function* openSidebar(objName, expectedObj, webconsole) { + let msg = yield webconsole.jsterm.execute(objName); + ok(msg, "output message found"); + + let anchor = msg.querySelector("a"); + let body = msg.querySelector(".message-body"); + ok(anchor, "object anchor"); + ok(body, "message body"); + + yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, webconsole.iframeWindow); + + let vviewVar = yield webconsole.jsterm.once("variablesview-fetched"); + let vview = vviewVar._variablesView; + ok(vview, "variables view object exists"); + + yield findVariableViewProperties(vviewVar, [ + expectedObj, + ], { webconsole: webconsole }); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js b/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js new file mode 100644 index 000000000..57d81fd05 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js @@ -0,0 +1,103 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// When strings containing URLs are entered into the webconsole, check +// its output and ensure that the output can be clicked to open those URLs. + +"use strict"; + +const inputTests = [ + + // 0: URL opens page when clicked. + { + input: "'http://example.com'", + output: "http://example.com", + expectedTab: "http://example.com/", + }, + + // 1: URL opens page using https when clicked. + { + input: "'https://example.com'", + output: "https://example.com", + expectedTab: "https://example.com/", + }, + + // 2: URL with port opens page when clicked. + { + input: "'https://example.com:443'", + output: "https://example.com:443", + expectedTab: "https://example.com/", + }, + + // 3: URL containing non-empty path opens page when clicked. + { + input: "'http://example.com/foo'", + output: "http://example.com/foo", + expectedTab: "http://example.com/foo", + }, + + // 4: URL opens page when clicked, even when surrounded by non-URL tokens. + { + input: "'foo http://example.com bar'", + output: "foo http://example.com bar", + expectedTab: "http://example.com/", + }, + + // 5: URL opens page when clicked, and whitespace is be preserved. + { + input: "'foo\\nhttp://example.com\\nbar'", + output: "foo\nhttp://example.com\nbar", + expectedTab: "http://example.com/", + }, + + // 6: URL opens page when clicked when multiple links are present. + { + input: "'http://example.com http://example.com'", + output: "http://example.com http://example.com", + expectedTab: "http://example.com/", + }, + + // 7: URL without scheme does not open page when clicked. + { + input: "'example.com'", + output: "example.com", + }, + + // 8: URL with invalid scheme does not open page when clicked. + { + input: "'foo://example.com'", + output: "foo://example.com", + }, + + // 9: Shortened URL in an array + { + input: "['http://example.com/abcdefghijabcdefghij some other text']", + output: "Array [ \"http://example.com/abcdefghijabcdef\u2026\" ]", + printOutput: "http://example.com/abcdefghijabcdefghij some other text", + expectedTab: "http://example.com/abcdefghijabcdefghij", + getClickableNode: (msg) => msg.querySelectorAll("a")[1], + }, + + // 10: Shortened URL in an object + { + input: "{test: 'http://example.com/abcdefghijabcdefghij some other text'}", + output: "Object { test: \"http://example.com/abcdefghijabcdef\u2026\" }", + printOutput: "[object Object]", + evalOutput: "http://example.com/abcdefghijabcdefghij some other text", + noClick: true, + consoleLogClick: true, + expectedTab: "http://example.com/abcdefghijabcdefghij", + getClickableNode: (msg) => msg.querySelectorAll("a")[1], + }, + +]; + +const url = "data:text/html;charset=utf8,Bug 1005909 - Clickable URLS"; + +add_task(function* () { + yield BrowserTestUtils.openNewForegroundTab(gBrowser, url); + let hud = yield openConsole(); + yield checkOutputForInputs(hud, inputTests); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js new file mode 100644 index 000000000..6a29d61aa --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js @@ -0,0 +1,100 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that inspecting a closure in the variables view sidebar works when +// execution is paused. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-closures.html"; + +var gWebConsole, gJSTerm, gVariablesView; + +// Force the old debugger UI since it's directly used (see Bug 1301705) +Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +function test() { + registerCleanupFunction(() => { + gWebConsole = gJSTerm = gVariablesView = null; + }); + + function fetchScopes(hud, toolbox, panelWin, deferred) { + panelWin.once(panelWin.EVENTS.FETCHED_SCOPES, () => { + ok(true, "Scopes were fetched"); + toolbox.selectTool("webconsole").then(() => consoleOpened(hud)); + deferred.resolve(); + }); + } + + loadTab(TEST_URI).then(() => { + openConsole().then((hud) => { + openDebugger().then(({ toolbox, panelWin }) => { + let deferred = promise.defer(); + fetchScopes(hud, toolbox, panelWin, deferred); + + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + let button = content.document.querySelector("button"); + ok(button, "button element found"); + button.click(); + }); + + return deferred.promise; + }); + }); + }); +} + +function consoleOpened(hud) { + gWebConsole = hud; + gJSTerm = hud.jsterm; + gJSTerm.execute("window.george.getName"); + + waitForMessages({ + webconsole: gWebConsole, + messages: [{ + text: "function _pfactory/<.getName()", + category: CATEGORY_OUTPUT, + objects: true, + }], + }).then(onExecuteGetName); +} + +function onExecuteGetName(results) { + let clickable = results[0].clickableElements[0]; + ok(clickable, "clickable object found"); + + gJSTerm.once("variablesview-fetched", onGetNameFetch); + let contextMenu = + gWebConsole.iframeWindow.document.getElementById("output-contextmenu"); + waitForContextMenu(contextMenu, clickable, () => { + let openInVarView = contextMenu.querySelector("#menu_openInVarView"); + ok(openInVarView.disabled === false, + "the \"Open In Variables View\" context menu item should be clickable"); + // EventUtils.synthesizeMouseAtCenter seems to fail here in Mac OSX + openInVarView.click(); + }); +} + +function onGetNameFetch(evt, view) { + gVariablesView = view._variablesView; + ok(gVariablesView, "variables view object"); + + findVariableViewProperties(view, [ + { name: /_pfactory/, value: "" }, + ], { webconsole: gWebConsole }).then(onExpandClosure); +} + +function onExpandClosure(results) { + let prop = results[0].matchedProp; + ok(prop, "matched the name property in the variables view"); + + gVariablesView.window.focus(); + gJSTerm.once("sidebar-closed", finishTest); + EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_column_numbers.js b/devtools/client/webconsole/test/browser_webconsole_column_numbers.js new file mode 100644 index 000000000..8407e34d5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_column_numbers.js @@ -0,0 +1,46 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + // Check if console provides the right column number alongside line number + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-column.html"; + +var hud; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} + +function consoleOpened(aHud) { + hud = aHud; + + waitForMessages({ + webconsole: hud, + messages: [{ + text: "Error Message", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR + }] + }).then(testLocationColumn); +} + +function testLocationColumn() { + let messages = hud.outputNode.children; + let expected = ["10:7", "10:39", "11:9", "12:11", "13:9", "14:7"]; + + for (let i = 0, len = messages.length; i < len; i++) { + let msg = messages[i].textContent; + + is(msg.includes(expected[i]), true, "Found expected line:column of " + + expected[i]); + } + + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_completion.js b/devtools/client/webconsole/test/browser_webconsole_completion.js new file mode 100644 index 000000000..ee0c6809e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_completion.js @@ -0,0 +1,106 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that code completion works properly. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test code completion"; + +var jsterm; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + jsterm = hud.jsterm; + let input = jsterm.inputNode; + + // Test typing 'docu'. + input.value = "docu"; + input.setSelectionRange(4, 4); + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(input.value, "docu", "'docu' completion (input.value)"); + is(jsterm.completeNode.value, " ment", "'docu' completion (completeNode)"); + + // Test typing 'docu' and press tab. + input.value = "docu"; + input.setSelectionRange(4, 4); + yield complete(jsterm.COMPLETE_FORWARD); + + is(input.value, "document", "'docu' tab completion"); + is(input.selectionStart, 8, "start selection is alright"); + is(input.selectionEnd, 8, "end selection is alright"); + is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed"); + + // Test typing 'window.Ob' and press tab. Just 'window.O' is + // ambiguous: could be window.Object, window.Option, etc. + input.value = "window.Ob"; + input.setSelectionRange(9, 9); + yield complete(jsterm.COMPLETE_FORWARD); + + is(input.value, "window.Object", "'window.Ob' tab completion"); + + // Test typing 'document.getElem'. + input.value = "document.getElem"; + input.setSelectionRange(16, 16); + yield complete(jsterm.COMPLETE_FORWARD); + + is(input.value, "document.getElem", "'document.getElem' completion"); + is(jsterm.completeNode.value, " entsByTagNameNS", + "'document.getElem' completion"); + + // Test pressing tab another time. + yield jsterm.complete(jsterm.COMPLETE_FORWARD); + + is(input.value, "document.getElem", "'document.getElem' completion"); + is(jsterm.completeNode.value, " entsByTagName", + "'document.getElem' another tab completion"); + + // Test pressing shift_tab. + complete(jsterm.COMPLETE_BACKWARD); + + is(input.value, "document.getElem", "'document.getElem' untab completion"); + is(jsterm.completeNode.value, " entsByTagNameNS", + "'document.getElem' completion"); + + jsterm.clearOutput(); + + input.value = "docu"; + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(jsterm.completeNode.value, " ment", "'docu' completion"); + yield jsterm.execute(); + is(jsterm.completeNode.value, "", "clear completion on execute()"); + + // Test multi-line completion works + input.value = "console.log('one');\nconsol"; + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(jsterm.completeNode.value, " \n e", + "multi-line completion"); + + // Test non-object autocompletion. + input.value = "Object.name.sl"; + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(jsterm.completeNode.value, " ice", "non-object completion"); + + // Test string literal autocompletion. + input.value = "'Asimov'.sl"; + yield complete(jsterm.COMPLETE_HINT_ONLY); + + is(jsterm.completeNode.value, " ice", "string literal completion"); + + jsterm = null; +}); + +function complete(type) { + let updated = jsterm.once("autocomplete-updated"); + jsterm.complete(type); + return updated; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js new file mode 100644 index 000000000..f8f02aa15 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js @@ -0,0 +1,85 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the console API messages for console.error()/exception()/assert() +// include a stackframe. See bug 920116. + +function test() { + let hud; + + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-api-stackframe.html"; + const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/")); + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + const stack = [{ + file: TEST_FILE, + fn: "thirdCall", + // 21,22,23 + line: /\b2[123]\b/, + }, { + file: TEST_FILE, + fn: "secondCall", + line: 16, + }, { + file: TEST_FILE, + fn: "firstCall", + line: 12, + }]; + + let results = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo-log", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + collapsible: false, + }, { + text: "foo-error", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: stack, + }, { + text: "foo-exception", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: stack, + }, { + text: "foo-assert", + category: CATEGORY_WEBDEV, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: stack, + }], + }); + + let elem = [...results[1].matched][0]; + ok(elem, "message element"); + + let msg = elem._messageObject; + ok(msg, "message object"); + + ok(msg.collapsed, "message is collapsed"); + + msg.toggleDetails(); + + ok(!msg.collapsed, "message is not collapsed"); + + msg.toggleDetails(); + + ok(msg.collapsed, "message is collapsed"); + + yield closeConsole(tab); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js b/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js new file mode 100644 index 000000000..310d4fc8b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js @@ -0,0 +1,81 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the '%c' modifier works with the console API. See bug 823097. + +function test() { + let hud; + + const TEST_URI = "data:text/html;charset=utf8,<p>test for " + + "console.log('%ccustom styles', 'color:red')"; + + const checks = [{ + // check the basics work + style: "color:red;font-size:1.3em", + props: { color: true, fontSize: true }, + sameStyleExpected: true, + }, { + // check that the url() is not allowed + style: "color:blue;background-image:url('http://example.com/test')", + props: { color: true, fontSize: false, background: false, + backgroundImage: false }, + sameStyleExpected: false, + }, { + // check that some properties are not allowed + style: "color:pink;position:absolute;top:10px", + props: { color: true, position: false, top: false }, + sameStyleExpected: false, + }]; + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + for (let check of checks) { + yield checkStyle(check); + } + + yield closeConsole(tab); + } + + function* checkStyle(check) { + hud.jsterm.clearOutput(); + + info("checkStyle " + check.style); + hud.jsterm.execute("console.log('%cfoobar', \"" + check.style + "\")"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobar", + category: CATEGORY_WEBDEV, + }], + }); + + let msg = [...result.matched][0]; + ok(msg, "message element"); + + let span = msg.querySelector(".message-body span[style]"); + ok(span, "span element"); + + info("span textContent is: " + span.textContent); + isnot(span.textContent.indexOf("foobar"), -1, "span textContent check"); + + let outputStyle = span.getAttribute("style").replace(/\s+|;+$/g, ""); + if (check.sameStyleExpected) { + is(outputStyle, check.style, "span style is correct"); + } else { + isnot(outputStyle, check.style, "span style is not the same"); + } + + for (let prop of Object.keys(check.props)) { + is(!!span.style[prop], check.props[prop], "property check for " + prop); + } + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_console_extras.js b/devtools/client/webconsole/test/browser_webconsole_console_extras.js new file mode 100644 index 000000000..078e33119 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_extras.js @@ -0,0 +1,43 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that window.console functions that are not implemented yet do not +// output anything in the web console and they do not throw any exceptions. +// See bug 614350. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-extras.html"; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(consoleOpened); + }); +} + +function consoleOpened(hud) { + waitForMessages({ + webconsole: hud, + messages: [{ + text: "start", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "end", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + let nodes = hud.outputNode.querySelectorAll(".message"); + is(nodes.length, 2, "only two messages are displayed"); + finishTest(); + }); + + let button = content.document.querySelector("button"); + ok(button, "we have the button"); + EventUtils.sendMouseEvent({ type: "click" }, button, content); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js b/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js new file mode 100644 index 000000000..317337543 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js @@ -0,0 +1,102 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the basic console.log()-style APIs and filtering work. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let outputNode = hud.outputNode; + + let methods = ["log", "info", "warn", "error", "exception", "debug"]; + for (let method of methods) { + yield testMethod(method, hud, outputNode); + } +}); + +function* testMethod(method, hud, outputNode) { + let console = content.console; + + hud.jsterm.clearOutput(); + + console[method]("foo-bar-baz"); + console[method]("baar-baz"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo-bar-baz", + }, { + text: "baar-baz", + }], + }); + + setStringFilter("foo", hud); + + is(outputNode.querySelectorAll(".filtered-by-string").length, 1, + "1 hidden " + method + " node via string filtering"); + + hud.jsterm.clearOutput(); + + // now toggle the current method off - make sure no visible message + // TODO: move all filtering tests into a separate test file: see bug 608135 + + console[method]("foo-bar-baz"); + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo-bar-baz", + }], + }); + + setStringFilter("", hud); + let filter; + switch (method) { + case "debug": + filter = "log"; + break; + case "exception": + filter = "error"; + break; + default: + filter = method; + break; + } + + hud.setFilterState(filter, false); + + is(outputNode.querySelectorAll(".filtered-by-type").length, 1, + "1 message hidden for " + method + " (logging turned off)"); + + hud.setFilterState(filter, true); + + is(outputNode.querySelectorAll(".message:not(.filtered-by-type)").length, 1, + "1 message shown for " + method + " (logging turned on)"); + + hud.jsterm.clearOutput(); + + // test for multiple arguments. + console[method]("foo", "bar"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo bar", + category: CATEGORY_WEBDEV, + }], + }); +} + +function setStringFilter(value, hud) { + hud.ui.filterBox.value = value; + hud.ui.adjustVisibilityOnSearchStringChange(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js b/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js new file mode 100644 index 000000000..9575721c3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the basic console.log()-style APIs and filtering work for +// sharedWorkers + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-workers.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foo-bar-shared-worker" + }], + }); + + hud.setFilterState("sharedworkers", false); + + is(hud.outputNode.querySelectorAll(".filtered-by-type").length, 1, + "1 message hidden for sharedworkers (logging turned off)"); + + hud.setFilterState("sharedworkers", true); + + is(hud.outputNode.querySelectorAll(".filtered-by-type").length, 0, + "1 message shown for sharedworkers (logging turned on)"); + + hud.setFilterState("sharedworkers", false); + + hud.jsterm.clearOutput(true); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js b/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js new file mode 100644 index 000000000..10c3ff7a5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js @@ -0,0 +1,75 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-console-trace-async.html"; + +add_task(function* runTest() { + // Async stacks aren't on by default in all builds + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": [ + ["javascript.options.asyncstack", true] + ]}, resolve); + }); + + let {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello"); + let hud = yield openConsole(tab); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.trace output", + consoleTrace: { + file: "test-console-trace-async.html", + fn: "inner", + }, + }], + }); + + let node = [...result.matched][0]; + ok(node, "found trace log node"); + ok(node.textContent.includes("console.trace()"), + "trace log node includes console.trace()"); + ok(node.textContent.includes("promise callback"), + "trace log node includes promise callback"); + ok(node.textContent.includes("setTimeout handler"), + "trace log node includes setTimeout handler"); + + // The expected stack trace object. + let stacktrace = [ + { + columnNumber: 3, + filename: TEST_URI, + functionName: "inner", + language: 2, + lineNumber: 9 + }, + { + asyncCause: "promise callback", + columnNumber: 3, + filename: TEST_URI, + functionName: "time1", + language: 2, + lineNumber: 13, + }, + { + asyncCause: "setTimeout handler", + columnNumber: 1, + filename: TEST_URI, + functionName: "", + language: 2, + lineNumber: 18, + } + ]; + + let obj = node._messageObject; + ok(obj, "console.trace message object"); + ok(obj._stacktrace, "found stacktrace object"); + is(obj._stacktrace.toSource(), stacktrace.toSource(), + "stacktrace is correct"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js b/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js new file mode 100644 index 000000000..e1c6f966e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js @@ -0,0 +1,50 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug_939783_console_trace_duplicates.html"; + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello"); + const hud = yield openConsole(tab); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI); + + // NB: Now that stack frames include a column number multiple invocations + // on the same line are considered unique. ie: + // |foo(); foo();| + // will generate two distinct trace entries. + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.trace output for foo1()", + text: "foo1", + consoleTrace: { + file: "test-bug_939783_console_trace_duplicates.html", + fn: "foo3", + }, + }, { + name: "console.trace output for foo1()", + text: "foo1", + consoleTrace: { + file: "test-bug_939783_console_trace_duplicates.html", + fn: "foo3", + }, + }, { + name: "console.trace output for foo1b()", + text: "foo1b", + consoleTrace: { + file: "test-bug_939783_console_trace_duplicates.html", + fn: "foo3", + }, + }], + }); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js b/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js new file mode 100644 index 000000000..8451ec762 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js @@ -0,0 +1,51 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the "Open in Variables View" context menu item is enabled +// only for objects. + +"use strict"; + +const TEST_URI = `data:text/html,<script> + console.log("foo"); + console.log("foo", window); +</script>`; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 2, + text: /foo/ + }], + }); + + let [msgWithText, msgWithObj] = [...result.matched]; + ok(msgWithText && msgWithObj, "Two messages should have appeared"); + + let contextMenu = hud.iframeWindow.document + .getElementById("output-contextmenu"); + let openInVarViewItem = contextMenu.querySelector("#menu_openInVarView"); + let obj = msgWithObj.querySelector(".cm-variable"); + let text = msgWithText.querySelector(".console-string"); + + yield waitForContextMenu(contextMenu, obj, () => { + ok(openInVarViewItem.disabled === false, "The \"Open In Variables View\" " + + "context menu item should be available for objects"); + }, () => { + ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " + + "context menu item should be disabled on popup hiding"); + }); + + yield waitForContextMenu(contextMenu, text, () => { + ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " + + "context menu item should be disabled for texts"); + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js b/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js new file mode 100644 index 000000000..4508101ee --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests the "Store as global variable" context menu item feature. +// It should be work, and be enabled only for objects + +"use strict"; + +const TEST_URI = `data:text/html,<script> + window.bar = { baz: 1 }; + console.log("foo"); + console.log("foo", window.bar); +</script>`; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 2, + text: /foo/ + }], + }); + + let [msgWithText, msgWithObj] = [...result.matched]; + ok(msgWithText && msgWithObj, "Two messages should have appeared"); + + let contextMenu = hud.iframeWindow.document + .getElementById("output-contextmenu"); + let storeAsGlobalItem = contextMenu.querySelector("#menu_storeAsGlobal"); + let obj = msgWithObj.querySelector(".cm-variable"); + let text = msgWithText.querySelector(".console-string"); + let onceInputSet = hud.jsterm.once("set-input-value"); + + info("Waiting for context menu on the object"); + yield waitForContextMenu(contextMenu, obj, () => { + ok(storeAsGlobalItem.disabled === false, "The \"Store as global\" " + + "context menu item should be available for objects"); + storeAsGlobalItem.click(); + }, () => { + ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " + + "context menu item should be disabled on popup hiding"); + }); + + info("Waiting for context menu on the text node"); + yield waitForContextMenu(contextMenu, text, () => { + ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " + + "context menu item should be disabled for texts"); + }); + + info("Waiting for input to be set"); + yield onceInputSet; + + is(hud.jsterm.getInputValue(), "temp0", "Input was set"); + let executedResult = yield hud.jsterm.execute(); + + ok(executedResult.textContent.includes("{ baz: 1 }"), + "Correct variable assigned into console"); + +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_count.js b/devtools/client/webconsole/test/browser_webconsole_count.js new file mode 100644 index 000000000..abb31a08d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_count.js @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that console.count() counts as expected. See bug 922208. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-count.html"; + +function test() { + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + + BrowserTestUtils.synthesizeMouseAtCenter("#local", {}, gBrowser.selectedBrowser); + let messages = []; + [ + "start", + "<no label>: 2", + "console.count() testcounter: 1", + "console.count() testcounter: 2", + "console.count() testcounter: 3", + "console.count() testcounter: 4", + "end" + ].forEach(function (msg) { + messages.push({ + text: msg, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG + }); + }); + messages.push({ + name: "Three local counts with no label and count=1", + text: "<no label>: 1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 3 + }); + yield waitForMessages({ + webconsole: hud, + messages: messages + }); + + hud.jsterm.clearOutput(); + + BrowserTestUtils.synthesizeMouseAtCenter("#external", {}, gBrowser.selectedBrowser); + messages = []; + [ + "start", + "console.count() testcounter: 5", + "console.count() testcounter: 6", + "end" + ].forEach(function (msg) { + messages.push({ + text: msg, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG + }); + }); + messages.push({ + name: "Two external counts with no label and count=1", + text: "<no label>: 1", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + count: 2 + }); + yield waitForMessages({ + webconsole: hud, + messages: messages + }); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js b/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js new file mode 100644 index 000000000..61ac68208 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js @@ -0,0 +1,56 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that if a link in console is double clicked, the console frame doesn't +// navigate to that destination (bug 975707). + +"use strict"; + +function test() { + let originalNetPref = Services.prefs + .getBoolPref("devtools.webconsole.filter.networkinfo"); + registerCleanupFunction(() => { + Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", + originalNetPref); + }); + Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true); + Task.spawn(runner).then(finishTest); + + function* runner() { + const TEST_PAGE_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-console.html" + "?_uniq=" + + Date.now(); + + const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello</p>"); + const hud = yield openConsole(tab); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PAGE_URI); + + let messages = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "Network request message", + url: TEST_PAGE_URI, + category: CATEGORY_NETWORK + }] + }); + + let networkEventMessage = messages[0].matched.values().next().value; + let urlNode = networkEventMessage.querySelector(".url"); + + let deferred = promise.defer(); + urlNode.addEventListener("click", function onClick(event) { + urlNode.removeEventListener("click", onClick); + ok(event.defaultPrevented, "The default action was prevented."); + + deferred.resolve(); + }); + + EventUtils.synthesizeMouseAtCenter(urlNode, {clickCount: 2}, + hud.iframeWindow); + + yield deferred.promise; + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js new file mode 100644 index 000000000..5cedfbad5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js @@ -0,0 +1,104 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the console receive exceptions include a stackframe. +// See bug 1184172. + +// On e10s, the exception is triggered in child process +// and is ignored by test harness +if (!Services.appinfo.browserTabsRemoteAutostart) { + SimpleTest.ignoreAllUncaughtExceptions(); +} + +function test() { + let hud; + + const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-exception-stackframe.html"; + const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/")); + + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab(TEST_URI); + hud = yield openConsole(tab); + + const stack = [{ + file: TEST_FILE, + fn: "thirdCall", + line: 21, + }, { + file: TEST_FILE, + fn: "secondCall", + line: 17, + }, { + file: TEST_FILE, + fn: "firstCall", + line: 12, + }]; + + let results = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "nonExistingMethodCall is not defined", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: stack, + }, { + text: "SyntaxError: 'buggy;selector' is not a valid selector", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: [{ + file: TEST_FILE, + fn: "domAPI", + line: 25, + }, { + file: TEST_FILE, + fn: "onLoadDomAPI", + line: 33, + } + ] + }, { + text: "DOMException", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + collapsible: true, + stacktrace: [{ + file: TEST_FILE, + fn: "domException", + line: 29, + }, { + file: TEST_FILE, + fn: "onLoadDomException", + line: 36, + }, + + ] + }], + }); + + let elem = [...results[0].matched][0]; + ok(elem, "message element"); + + let msg = elem._messageObject; + ok(msg, "message object"); + + ok(msg.collapsed, "message is collapsed"); + + msg.toggleDetails(); + + ok(!msg.collapsed, "message is not collapsed"); + + msg.toggleDetails(); + + ok(msg.collapsed, "message is collapsed"); + + yield closeConsole(tab); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_execution_scope.js b/devtools/client/webconsole/test/browser_webconsole_execution_scope.js new file mode 100644 index 000000000..78865c9b2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_execution_scope.js @@ -0,0 +1,37 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that commands run by the user are executed in content space. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + hud.jsterm.execute("window.location.href;"); + + let [input, output] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "window.location.href;", + category: CATEGORY_INPUT, + }, + { + text: TEST_URI, + category: CATEGORY_OUTPUT, + }], + }); + + let inputNode = [...input.matched][0]; + let outputNode = [...output.matched][0]; + is(inputNode.getAttribute("category"), "input", + "input node category is correct"); + is(outputNode.getAttribute("category"), "output", + "output node category is correct"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js b/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js new file mode 100644 index 000000000..192387e8a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js @@ -0,0 +1,57 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test for the message timestamps option: check if the preference toggles the +// display of messages in the console output. See bug 722267. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "bug 722267 - preference for toggling timestamps in messages"; +const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages"; +var hud; + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + let panel = yield consoleOpened(); + + yield onOptionsPanelSelected(panel); + onPrefChanged(); + + Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP); + hud = null; +}); + +function consoleOpened() { + info("console opened"); + let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP); + ok(!prefValue, "messages have no timestamp by default (pref check)"); + ok(hud.outputNode.classList.contains("hideTimestamps"), + "messages have no timestamp (class name check)"); + + let toolbox = gDevTools.getToolbox(hud.target); + return toolbox.selectTool("options"); +} + +function onOptionsPanelSelected(panel) { + info("options panel opened"); + + let prefChanged = gDevTools.once("pref-changed", onPrefChanged); + + let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages"); + checkbox.click(); + + return prefChanged; +} + +function onPrefChanged() { + info("pref changed"); + let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP); + ok(prefValue, "messages have timestamps (pref check)"); + ok(!hud.outputNode.classList.contains("hideTimestamps"), + "messages have timestamps (class name check)"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js b/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js new file mode 100644 index 000000000..e210bd81a --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js @@ -0,0 +1,95 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the filter button context menu logic works correctly. + +"use strict"; + +const TEST_URI = "http://example.com/"; + +function test() { + loadTab(TEST_URI).then(() => { + openConsole().then(testFilterButtons); + }); +} + +function testFilterButtons(aHud) { + let hudBox = aHud.ui.rootElement; + + testRightClick("net", hudBox, aHud) + .then(() => testRightClick("css", hudBox, aHud)) + .then(() => testRightClick("js", hudBox, aHud)) + .then(() => testRightClick("logging", hudBox, aHud)) + .then(() => testRightClick("security", hudBox, aHud)) + .then(finishTest); +} + +function testRightClick(aCategory, hudBox, aHud) { + let deferred = promise.defer(); + let selector = ".webconsole-filter-button[category=\"" + aCategory + "\"]"; + let button = hudBox.querySelector(selector); + let mainButton = getMainButton(button, aHud); + let origCheckedState = button.getAttribute("aria-pressed"); + let contextMenu = aHud.iframeWindow.document.getElementById(aCategory + "-contextmenu"); + + function verifyContextMenuIsClosed() { + info("verify the context menu is closed"); + is(button.getAttribute("open"), false, "The context menu for the \"" + + aCategory + "\" button is closed"); + } + + function verifyOriginalCheckedState() { + info("verify the button has the original checked state"); + is(button.getAttribute("aria-pressed"), origCheckedState, + "The button state should not have changed"); + } + + function verifyNewCheckedState() { + info("verify the button's checked state has changed"); + isnot(button.getAttribute("aria-pressed"), origCheckedState, + "The button state should have changed"); + } + + function leftClickToClose() { + info("left click the button to close the contextMenu"); + EventUtils.sendMouseEvent({type: "click"}, button); + executeSoon(() => { + verifyContextMenuIsClosed(); + verifyOriginalCheckedState(); + leftClickToChangeCheckedState(); + }); + } + + function leftClickToChangeCheckedState() { + info("left click the mainbutton to change checked state"); + EventUtils.sendMouseEvent({type: "click"}, mainButton); + executeSoon(() => { + verifyContextMenuIsClosed(); + verifyNewCheckedState(); + deferred.resolve(); + }); + } + + verifyContextMenuIsClosed(); + info("right click the button to open the context menu"); + waitForContextMenu(contextMenu, mainButton, verifyOriginalCheckedState, + leftClickToClose); + return deferred.promise; +} + +function getMainButton(aTargetButton, aHud) { + let anonymousNodes = aHud.ui.document.getAnonymousNodes(aTargetButton); + let subbutton; + + for (let i = 0; i < anonymousNodes.length; i++) { + let node = anonymousNodes[i]; + if (node.classList.contains("toolbarbutton-menubutton-button")) { + subbutton = node; + break; + } + } + + return subbutton; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_for_of.js b/devtools/client/webconsole/test/browser_webconsole_for_of.js new file mode 100644 index 000000000..83d3aaa3d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_for_of.js @@ -0,0 +1,32 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// A for-of loop in Web Console code can loop over a content NodeList. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-for-of.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + yield testForOf(hud); +}); + +function testForOf(hud) { + let deferred = promise.defer(); + + let jsterm = hud.jsterm; + jsterm.execute("{ let _nodes = []; for (let x of document.body.childNodes) { if (x.nodeType === 1) { _nodes.push(x.tagName); } } _nodes.join(' '); }", + (node) => { + ok(/H1 DIV H2 P/.test(node.textContent), + "for-of loop should find all top-level nodes"); + deferred.resolve(); + }); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_history.js b/devtools/client/webconsole/test/browser_webconsole_history.js new file mode 100644 index 000000000..5ae709a4b --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_history.js @@ -0,0 +1,62 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests the console history feature accessed via the up and down arrow keys. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +// Constants used for defining the direction of JSTerm input history navigation. +const HISTORY_BACK = -1; +const HISTORY_FORWARD = 1; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let jsterm = hud.jsterm; + let input = jsterm.inputNode; + + let executeList = ["document", "window", "window.location"]; + + for (let item of executeList) { + input.value = item; + yield jsterm.execute(); + } + + for (let x = executeList.length - 1; x != -1; x--) { + jsterm.historyPeruse(HISTORY_BACK); + is(input.value, executeList[x], "check history previous idx:" + x); + } + + jsterm.historyPeruse(HISTORY_BACK); + is(input.value, executeList[0], "test that item is still index 0"); + + jsterm.historyPeruse(HISTORY_BACK); + is(input.value, executeList[0], "test that item is still still index 0"); + + for (let i = 1; i < executeList.length; i++) { + jsterm.historyPeruse(HISTORY_FORWARD); + is(input.value, executeList[i], "check history next idx:" + i); + } + + jsterm.historyPeruse(HISTORY_FORWARD); + is(input.value, "", "check input is empty again"); + + // Simulate pressing Arrow_Down a few times and then if Arrow_Up shows + // the previous item from history again. + jsterm.historyPeruse(HISTORY_FORWARD); + jsterm.historyPeruse(HISTORY_FORWARD); + jsterm.historyPeruse(HISTORY_FORWARD); + + is(input.value, "", "check input is still empty"); + + let idxLast = executeList.length - 1; + jsterm.historyPeruse(HISTORY_BACK); + is(input.value, executeList[idxLast], "check history next idx:" + idxLast); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js new file mode 100644 index 000000000..3ee33669d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js @@ -0,0 +1,126 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that errors about invalid HPKP security headers are logged to the web +// console. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console HPKP invalid " + + "header test"; +const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" + + "test/test_hpkp-invalid-headers.sjs"; +const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + + "Public_Key_Pinning" + DOCS_GA_PARAMS; +const NON_BUILTIN_ROOT_PREF = "security.cert_pinning.process_headers_from_" + + "non_builtin_roots"; + +add_task(function* () { + registerCleanupFunction(() => { + Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF); + }); + + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield* checkForMessage({ + url: SJS_URL + "?badSyntax", + name: "Could not parse header error displayed successfully", + text: "Public-Key-Pins: The site specified a header that could not be " + + "parsed successfully." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?noMaxAge", + name: "No max-age error displayed successfully", + text: "Public-Key-Pins: The site specified a header that did not include " + + "a \u2018max-age\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?invalidIncludeSubDomains", + name: "Invalid includeSubDomains error displayed successfully", + text: "Public-Key-Pins: The site specified a header that included an " + + "invalid \u2018includeSubDomains\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?invalidMaxAge", + name: "Invalid max-age error displayed successfully", + text: "Public-Key-Pins: The site specified a header that included an " + + "invalid \u2018max-age\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?multipleIncludeSubDomains", + name: "Multiple includeSubDomains error displayed successfully", + text: "Public-Key-Pins: The site specified a header that included " + + "multiple \u2018includeSubDomains\u2019 directives." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?multipleMaxAge", + name: "Multiple max-age error displayed successfully", + text: "Public-Key-Pins: The site specified a header that included " + + "multiple \u2018max-age\u2019 directives." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?multipleReportURIs", + name: "Multiple report-uri error displayed successfully", + text: "Public-Key-Pins: The site specified a header that included " + + "multiple \u2018report-uri\u2019 directives." + }, hud); + + // The root used for mochitests is not built-in, so set the relevant pref to + // true to have the PKP implementation return more specific errors. + Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, true); + + yield* checkForMessage({ + url: SJS_URL + "?pinsetDoesNotMatch", + name: "Non-matching pinset error displayed successfully", + text: "Public-Key-Pins: The site specified a header that did not include " + + "a matching pin." + }, hud); + + Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, false); + + yield* checkForMessage({ + url: SJS_URL + "?pinsetDoesNotMatch", + name: "Non-built-in root error displayed successfully", + text: "Public-Key-Pins: The certificate used by the site was not issued " + + "by a certificate in the default root certificate store. To " + + "prevent accidental breakage, the specified header was ignored." + }, hud); +}); + +function* checkForMessage(curTest, hud) { + hud.jsterm.clearOutput(); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, curTest.url); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: curTest.name, + text: curTest.text, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + ], + }); + + yield testClickOpenNewTab(hud, results); +} + +function testClickOpenNewTab(hud, results) { + let warningNode = results[0].clickableElements[0]; + ok(warningNode, "link element"); + ok(warningNode.classList.contains("learn-more-link"), "link class name"); + return simulateMessageLinkClick(warningNode, LEARN_MORE_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js b/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js new file mode 100644 index 000000000..19cedefdb --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js @@ -0,0 +1,92 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that errors about invalid HSTS security headers are logged +// to the web console. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console HSTS invalid " + + "header test"; +const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" + + "test/test_hsts-invalid-headers.sjs"; +const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" + + "HTTP_strict_transport_security" + DOCS_GA_PARAMS; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + yield* checkForMessage({ + url: SJS_URL + "?badSyntax", + name: "Could not parse header error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that could " + + "not be parsed successfully." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?noMaxAge", + name: "No max-age error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that did " + + "not include a \u2018max-age\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?invalidIncludeSubDomains", + name: "Invalid includeSubDomains error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that " + + "included an invalid \u2018includeSubDomains\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?invalidMaxAge", + name: "Invalid max-age error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that " + + "included an invalid \u2018max-age\u2019 directive." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?multipleIncludeSubDomains", + name: "Multiple includeSubDomains error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that " + + "included multiple \u2018includeSubDomains\u2019 directives." + }, hud); + + yield* checkForMessage({ + url: SJS_URL + "?multipleMaxAge", + name: "Multiple max-age error displayed successfully", + text: "Strict-Transport-Security: The site specified a header that " + + "included multiple \u2018max-age\u2019 directives." + }, hud); +}); + +function* checkForMessage(curTest, hud) { + hud.jsterm.clearOutput(); + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, curTest.url); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: curTest.name, + text: curTest.text, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + ], + }); + + yield testClickOpenNewTab(hud, results); +} + +function testClickOpenNewTab(hud, results) { + let warningNode = results[0].clickableElements[0]; + ok(warningNode, "link element"); + ok(warningNode.classList.contains("learn-more-link"), "link class name"); + return simulateMessageLinkClick(warningNode, LEARN_MORE_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js b/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js new file mode 100644 index 000000000..2d7fda7f5 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js @@ -0,0 +1,34 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that the JS input field is focused when the user switches back to the +// web console from other tools, see bug 891581. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>hello"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + is(hud.jsterm.inputNode.hasAttribute("focused"), true, + "inputNode should be focused"); + + hud.ui.filterBox.focus(); + + is(hud.ui.filterBox.hasAttribute("focused"), true, + "filterBox should be focused"); + + is(hud.jsterm.inputNode.hasAttribute("focused"), false, + "inputNode shouldn't be focused"); + + yield openInspector(); + hud = yield openConsole(); + + is(hud.jsterm.inputNode.hasAttribute("focused"), true, + "inputNode should be focused"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js b/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js new file mode 100644 index 000000000..f79ba386f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that dynamically created (HTML|XML|SVG)Documents can be inspected by +// clicking on the object in console (bug 1035198). + +"use strict"; + +const TEST_CASES = [ + { + input: '(new DOMParser()).parseFromString("<a />", "text/html")', + output: "HTMLDocument", + inspectable: true, + }, + { + input: '(new DOMParser()).parseFromString("<a />", "application/xml")', + output: "XMLDocument", + inspectable: true, + }, + { + input: '(new DOMParser()).parseFromString("<svg></svg>", "image/svg+xml")', + output: "XMLDocument", + inspectable: true, + }, +]; + +const TEST_URI = "data:text/html;charset=utf8," + + "browser_webconsole_inspect-parsed-documents.js"; +add_task(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + yield checkOutputForInputs(hud, TEST_CASES); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js b/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js new file mode 100644 index 000000000..7d45059fc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js @@ -0,0 +1,55 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the input box expands as the user types long lines. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let input = hud.jsterm.inputNode; + input.focus(); + + is(input.getAttribute("multiline"), "true", "multiline is enabled"); + // Tests if the inputNode expands. + input.value = "hello\nworld\n"; + let length = input.value.length; + input.selectionEnd = length; + input.selectionStart = length; + function getHeight() { + return input.clientHeight; + } + let initialHeight = getHeight(); + // Performs an "d". This will trigger/test for the input event that should + // change the "row" attribute of the inputNode. + EventUtils.synthesizeKey("d", {}); + let newHeight = getHeight(); + ok(initialHeight < newHeight, "Height changed: " + newHeight); + + // Add some more rows. Tests for the 8 row limit. + input.value = "row1\nrow2\nrow3\nrow4\nrow5\nrow6\nrow7\nrow8\nrow9\nrow10\n"; + length = input.value.length; + input.selectionEnd = length; + input.selectionStart = length; + EventUtils.synthesizeKey("d", {}); + let newerHeight = getHeight(); + + ok(newerHeight > newHeight, "height changed: " + newerHeight); + + // Test if the inputNode shrinks again. + input.value = ""; + EventUtils.synthesizeKey("d", {}); + let height = getHeight(); + info("height: " + height); + info("initialHeight: " + initialHeight); + let finalHeightDifference = Math.abs(initialHeight - height); + ok(finalHeightDifference <= 1, "height shrank to original size within 1px"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_jsterm.js b/devtools/client/webconsole/test/browser_webconsole_jsterm.js new file mode 100644 index 000000000..221c96fa6 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_jsterm.js @@ -0,0 +1,195 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +var jsterm; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + jsterm = hud.jsterm; + yield testJSTerm(hud); + jsterm = null; +}); + +function checkResult(msg, desc) { + let def = promise.defer(); + waitForMessages({ + webconsole: jsterm.hud.owner, + messages: [{ + name: desc, + category: CATEGORY_OUTPUT, + }], + }).then(([result]) => { + let node = [...result.matched][0].querySelector(".message-body"); + if (typeof msg == "string") { + is(node.textContent.trim(), msg, + "correct message shown for " + desc); + } else if (typeof msg == "function") { + ok(msg(node), "correct message shown for " + desc); + } + + def.resolve(); + }); + return def.promise; +} + +function* testJSTerm(hud) { + const HELP_URL = "https://developer.mozilla.org/docs/Tools/" + + "Web_Console/Helpers"; + + jsterm.clearOutput(); + yield jsterm.execute("$('#header').getAttribute('id')"); + yield checkResult('"header"', "$() worked"); + + jsterm.clearOutput(); + yield jsterm.execute("$$('h1').length"); + yield checkResult("1", "$$() worked"); + + jsterm.clearOutput(); + yield jsterm.execute("$x('.//*', document.body)[0] == $$('h1')[0]"); + yield checkResult("true", "$x() worked"); + + // no jsterm.clearOutput() here as we clear the output using the clear() fn. + yield jsterm.execute("clear()"); + + yield waitForSuccess({ + name: "clear() worked", + validator: function () { + return jsterm.outputNode.childNodes.length == 0; + } + }); + + jsterm.clearOutput(); + yield jsterm.execute("keys({b:1})[0] == 'b'"); + yield checkResult("true", "keys() worked", 1); + + jsterm.clearOutput(); + yield jsterm.execute("values({b:1})[0] == 1"); + yield checkResult("true", "values() worked", 1); + + jsterm.clearOutput(); + + let openedLinks = 0; + let oldOpenLink = hud.openLink; + hud.openLink = (url) => { + if (url == HELP_URL) { + openedLinks++; + } + }; + + yield jsterm.execute("help()"); + yield jsterm.execute("help"); + yield jsterm.execute("?"); + + let output = jsterm.outputNode.querySelector(".message[category='output']"); + ok(!output, "no output for help() calls"); + is(openedLinks, 3, "correct number of pages opened by the help calls"); + hud.openLink = oldOpenLink; + + jsterm.clearOutput(); + yield jsterm.execute("pprint({b:2, a:1})"); + yield checkResult("\" b: 2\n a: 1\"", "pprint()"); + + // check instanceof correctness, bug 599940 + jsterm.clearOutput(); + yield jsterm.execute("[] instanceof Array"); + yield checkResult("true", "[] instanceof Array == true"); + + jsterm.clearOutput(); + yield jsterm.execute("({}) instanceof Object"); + yield checkResult("true", "({}) instanceof Object == true"); + + // check for occurrences of Object XRayWrapper, bug 604430 + jsterm.clearOutput(); + yield jsterm.execute("document"); + yield checkResult(function (node) { + return node.textContent.search(/\[object xraywrapper/i) == -1; + }, "document - no XrayWrapper"); + + // check that pprint(window) and keys(window) don't throw, bug 608358 + jsterm.clearOutput(); + yield jsterm.execute("pprint(window)"); + yield checkResult(null, "pprint(window)"); + + jsterm.clearOutput(); + yield jsterm.execute("keys(window)"); + yield checkResult(null, "keys(window)"); + + // bug 614561 + jsterm.clearOutput(); + yield jsterm.execute("pprint('hi')"); + yield checkResult("\" 0: \"h\"\n 1: \"i\"\"", "pprint('hi')"); + + // check that pprint(function) shows function source, bug 618344 + jsterm.clearOutput(); + yield jsterm.execute("pprint(function() { var someCanaryValue = 42; })"); + yield checkResult(function (node) { + return node.textContent.indexOf("someCanaryValue") > -1; + }, "pprint(function) shows source"); + + // check that an evaluated null produces "null", bug 650780 + jsterm.clearOutput(); + yield jsterm.execute("null"); + yield checkResult("null", "null is null"); + + jsterm.clearOutput(); + yield jsterm.execute("undefined"); + yield checkResult("undefined", "undefined is printed"); + + // check that thrown strings produce error messages, + // and the message text matches that of a stringified error object + // bug 1099071 + jsterm.clearOutput(); + yield jsterm.execute("throw '';"); + yield checkResult((node) => { + return node.closest(".message").getAttribute("severity") === "error" && + node.textContent === new Error("").toString(); + }, "thrown empty string generates error message"); + + jsterm.clearOutput(); + yield jsterm.execute("throw 'tomatoes';"); + yield checkResult((node) => { + return node.closest(".message").getAttribute("severity") === "error" && + node.textContent === new Error("tomatoes").toString(); + }, "thrown non-empty string generates error message"); + + jsterm.clearOutput(); + yield jsterm.execute("throw { foo: 'bar' };"); + yield checkResult((node) => { + return node.closest(".message").getAttribute("severity") === "error" && + node.textContent === Object.prototype.toString(); + }, "thrown object generates error message"); + + // check that errors with entires in errordocs.js display links + // alongside their messages. + const ErrorDocs = require("devtools/server/actors/errordocs"); + + const ErrorDocStatements = { + "JSMSG_BAD_RADIX": "(42).toString(0);", + "JSMSG_BAD_ARRAY_LENGTH": "([]).length = -1", + "JSMSG_NEGATIVE_REPETITION_COUNT": "'abc'.repeat(-1);", + "JSMSG_BAD_FORMAL": "var f = Function('x y', 'return x + y;');", + "JSMSG_PRECISION_RANGE": "77.1234.toExponential(-1);", + }; + + for (let errorMessageName of Object.keys(ErrorDocStatements)) { + let title = ErrorDocs.GetURL({ errorMessageName }).split("?")[0]; + + jsterm.clearOutput(); + yield jsterm.execute(ErrorDocStatements[errorMessageName]); + yield checkResult((node) => { + return node.parentNode.getElementsByTagName("a")[0].title == title; + }, `error links to ${title}`); + } + + // Ensure that dom errors, with error numbers outside of the range + // of valid js.msg errors, don't cause crashes (bug 1270721). + yield jsterm.execute("new Request('',{redirect:'foo'})"); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js b/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js new file mode 100644 index 000000000..1dbfa80d9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js @@ -0,0 +1,56 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the message type filter checkboxes work. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + for (let i = 0; i < 50; i++) { + content.console.log("foobarz #" + i); + } + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "foobarz #49", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(hud.outputNode.children.length, 50, "number of messages"); + + hud.setFilterState("log", false); + is(countMessageNodes(hud), 0, "the log nodes are hidden when the " + + "corresponding filter is switched off"); + + hud.setFilterState("log", true); + is(countMessageNodes(hud), 50, "the log nodes reappear when the " + + "corresponding filter is switched on"); +}); + +function countMessageNodes(hud) { + let messageNodes = hud.outputNode.querySelectorAll(".message"); + let displayedMessageNodes = 0; + let view = hud.iframeWindow; + for (let i = 0; i < messageNodes.length; i++) { + let computedStyle = view.getComputedStyle(messageNodes[i], null); + if (computedStyle.display !== "none") { + displayedMessageNodes++; + } + } + + return displayedMessageNodes; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js b/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js new file mode 100644 index 000000000..d41d5cf2e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js @@ -0,0 +1,96 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the text filter box works. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () { + for (let i = 0; i < 50; i++) { + content.console.log("http://www.example.com/ " + i); + } + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "http://www.example.com/ 49", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + is(hud.outputNode.children.length, 50, "number of messages"); + + setStringFilter(hud, "http"); + isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " + + "search string is set to \"http\""); + + setStringFilter(hud, "hxxp"); + is(countMessageNodes(hud), 0, "the log nodes are hidden when the search " + + "string is set to \"hxxp\""); + + setStringFilter(hud, "ht tp"); + isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " + + "search string is set to \"ht tp\""); + + setStringFilter(hud, " zzzz zzzz "); + is(countMessageNodes(hud), 0, "the log nodes are hidden when the search " + + "string is set to \" zzzz zzzz \""); + + setStringFilter(hud, ""); + isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " + + "search string is removed"); + + setStringFilter(hud, "\u9f2c"); + is(countMessageNodes(hud), 0, "the log nodes are hidden when searching " + + "for weasels"); + + setStringFilter(hud, "\u0007"); + is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " + + "the bell character"); + + setStringFilter(hud, '"foo"'); + is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " + + 'the string "foo"'); + + setStringFilter(hud, "'foo'"); + is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " + + "the string 'foo'"); + + setStringFilter(hud, "foo\"bar'baz\"boo'"); + is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " + + "the string \"foo\"bar'baz\"boo'\""); +}); + +function countMessageNodes(hud) { + let outputNode = hud.outputNode; + + let messageNodes = outputNode.querySelectorAll(".message"); + let displayedMessageNodes = 0; + let view = hud.iframeWindow; + for (let i = 0; i < messageNodes.length; i++) { + let computedStyle = view.getComputedStyle(messageNodes[i], null); + if (computedStyle.display !== "none") { + displayedMessageNodes++; + } + } + + return displayedMessageNodes; +} + +function setStringFilter(hud, value) { + hud.ui.filterBox.value = value; + hud.ui.adjustVisibilityOnSearchStringChange(); +} + diff --git a/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js b/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js new file mode 100644 index 000000000..d5059485f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js @@ -0,0 +1,83 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the text filter box works to filter based on filenames +// where the logs were generated. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-bug_923281_console_log_filter.html"; + +var hud; + +add_task(function* () { + yield loadTab(TEST_URI); + + hud = yield openConsole(); + yield consoleOpened(); + + testLiveFilteringOnSearchStrings(); + + hud = null; +}); + +function consoleOpened() { + let console = content.console; + console.log("sentinel log"); + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "sentinel log", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG + }], + }); +} + +function testLiveFilteringOnSearchStrings() { + is(hud.outputNode.children.length, 4, "number of messages"); + + setStringFilter("random"); + is(countMessageNodes(), 1, "the log nodes not containing string " + + "\"random\" are hidden"); + + setStringFilter("test2.js"); + is(countMessageNodes(), 2, "show only log nodes containing string " + + "\"test2.js\" or log nodes created from files with filename " + + "containing \"test2.js\" as substring."); + + setStringFilter("test1"); + is(countMessageNodes(), 2, "show only log nodes containing string " + + "\"test1\" or log nodes created from files with filename " + + "containing \"test1\" as substring."); + + setStringFilter(""); + is(countMessageNodes(), 4, "show all log nodes on setting filter string " + + "as \"\"."); +} + +function countMessageNodes() { + let outputNode = hud.outputNode; + + let messageNodes = outputNode.querySelectorAll(".message"); + content.console.log(messageNodes.length); + let displayedMessageNodes = 0; + let view = hud.iframeWindow; + for (let i = 0; i < messageNodes.length; i++) { + let computedStyle = view.getComputedStyle(messageNodes[i], null); + if (computedStyle.display !== "none") { + displayedMessageNodes++; + } + } + + return displayedMessageNodes; +} + +function setStringFilter(value) { + hud.ui.filterBox.value = value; + hud.ui.adjustVisibilityOnSearchStringChange(); +} + diff --git a/devtools/client/webconsole/test/browser_webconsole_message_node_id.js b/devtools/client/webconsole/test/browser_webconsole_message_node_id.js new file mode 100644 index 000000000..bec657740 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_message_node_id.js @@ -0,0 +1,28 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + hud.jsterm.execute("console.log('a log message')"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "a log message", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let msg = [...result.matched][0]; + ok(msg.getAttribute("id"), "log message has an ID"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_multiline_input.js b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js new file mode 100644 index 000000000..7285c2127 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js @@ -0,0 +1,70 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Tests that the console waits for more input instead of evaluating +// when valid, but incomplete, statements are present upon pressing enter +// -or- when the user ends a line with shift + enter. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +let SHOULD_ENTER_MULTILINE = [ + {input: "function foo() {" }, + {input: "var a = 1," }, + {input: "var a = 1;", shiftKey: true }, + {input: "function foo() { }", shiftKey: true }, + {input: "function" }, + {input: "(x) =>" }, + {input: "let b = {" }, + {input: "let a = [" }, + {input: "{" }, + {input: "{ bob: 3343," }, + {input: "function x(y=" }, + {input: "Array.from(" }, + // shift + enter creates a new line despite parse errors + {input: "{2,}", shiftKey: true }, +]; +let SHOULD_EXECUTE = [ + {input: "function foo() { }" }, + {input: "var a = 1;" }, + {input: "function foo() { var a = 1; }" }, + {input: '"asdf"' }, + {input: "99 + 3" }, + {input: "1, 2, 3" }, + // errors + {input: "function f(x) { let y = 1, }" }, + {input: "function f(x=,) {" }, + {input: "{2,}" }, +]; + +add_task(function* () { + let { tab, browser } = yield loadTab(TEST_URI); + let hud = yield openConsole(); + let inputNode = hud.jsterm.inputNode; + + for (let test of SHOULD_ENTER_MULTILINE) { + hud.jsterm.setInputValue(test.input); + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey }); + let inputValue = hud.jsterm.getInputValue(); + is(inputNode.selectionStart, inputNode.selectionEnd, + "selection is collapsed"); + is(inputNode.selectionStart, inputValue.length, + "caret at end of multiline input"); + let inputWithNewline = test.input + "\n"; + is(inputValue, inputWithNewline, "Input value is correct"); + } + + for (let test of SHOULD_EXECUTE) { + hud.jsterm.setInputValue(test.input); + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey }); + let inputValue = hud.jsterm.getInputValue(); + is(inputNode.selectionStart, 0, "selection starts/ends at 0"); + is(inputNode.selectionEnd, 0, "selection starts/ends at 0"); + is(inputValue, "", "Input value is cleared"); + } + +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging.js b/devtools/client/webconsole/test/browser_webconsole_netlogging.js new file mode 100644 index 000000000..63730c9b4 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_netlogging.js @@ -0,0 +1,139 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests response logging for different request types. + +"use strict"; + +// This test runs very slowly on linux32 debug - bug 1269977 +requestLongerTimeout(2); + +const TEST_NETWORK_REQUEST_URI = + "http://example.com/browser/devtools/client/webconsole/test/" + + "test-network-request.html"; + +const TEST_DATA_JSON_CONTENT = + '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }'; + +const PAGE_REQUEST_PREDICATE = + ({ request }) => request.url.endsWith("test-network-request.html"); + +const TEST_DATA_REQUEST_PREDICATE = + ({ request }) => request.url.endsWith("test-data.json"); + +add_task(function* testPageLoad() { + // Enable logging in the UI. Not needed to pass test but makes it easier + // to debug interactively. + yield new Promise(resolve => { + SpecialPowers.pushPrefEnv({"set": + [["devtools.webconsole.filter.networkinfo", true] + ]}, resolve); + }); + + let finishedRequest = waitForFinishedRequest(PAGE_REQUEST_PREDICATE); + let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI); + let request = yield finishedRequest; + + ok(request, "Page load was logged"); + + let client = hud.ui.webConsoleClient; + let args = [request.actor]; + const postData = yield getPacket(client, "getRequestPostData", args); + const responseContent = yield getPacket(client, "getResponseContent", args); + + is(request.request.url, TEST_NETWORK_REQUEST_URI, + "Logged network entry is page load"); + is(request.request.method, "GET", "Method is correct"); + ok(!postData.postData.text, "No request body was stored"); + ok(!postData.postDataDiscarded, + "Request body was not discarded"); + is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0, + "Response body's beginning is okay"); + + yield closeTabAndToolbox(); +}); + +add_task(function* testXhrGet() { + let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI); + + let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE); + content.wrappedJSObject.testXhrGet(); + let request = yield finishedRequest; + + ok(request, "testXhrGet() was logged"); + + let client = hud.ui.webConsoleClient; + let args = [request.actor]; + const postData = yield getPacket(client, "getRequestPostData", args); + const responseContent = yield getPacket(client, "getResponseContent", args); + + is(request.request.method, "GET", "Method is correct"); + ok(!postData.postData.text, "No request body was sent"); + ok(!postData.postDataDiscarded, + "Request body was not discarded"); + is(responseContent.content.text, TEST_DATA_JSON_CONTENT, + "Response is correct"); + + yield closeTabAndToolbox(); +}); + +add_task(function* testXhrPost() { + let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI); + + let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE); + content.wrappedJSObject.testXhrPost(); + let request = yield finishedRequest; + + ok(request, "testXhrPost() was logged"); + + let client = hud.ui.webConsoleClient; + let args = [request.actor]; + const postData = yield getPacket(client, "getRequestPostData", args); + const responseContent = yield getPacket(client, "getResponseContent", args); + + is(request.request.method, "POST", "Method is correct"); + is(postData.postData.text, "Hello world!", "Request body was logged"); + is(responseContent.content.text, TEST_DATA_JSON_CONTENT, + "Response is correct"); + + yield closeTabAndToolbox(); +}); + +add_task(function* testFormSubmission() { + let pageLoadRequestFinished = waitForFinishedRequest(PAGE_REQUEST_PREDICATE); + let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI); + + info("Waiting for the page load to be finished."); + yield pageLoadRequestFinished; + + // The form POSTs to the page URL but over https (page over http). + let finishedRequest = waitForFinishedRequest(PAGE_REQUEST_PREDICATE); + ContentTask.spawn(gBrowser.selectedBrowser, { }, `function() + { + let form = content.document.querySelector("form"); + form.submit(); + }`); + let request = yield finishedRequest; + + ok(request, "testFormSubmission() was logged"); + + let client = hud.ui.webConsoleClient; + let args = [request.actor]; + const postData = yield getPacket(client, "getRequestPostData", args); + const responseContent = yield getPacket(client, "getResponseContent", args); + + is(request.request.method, "POST", "Method is correct"); + isnot(postData.postData.text + .indexOf("Content-Type: application/x-www-form-urlencoded"), -1, + "Content-Type is correct"); + isnot(postData.postData.text + .indexOf("Content-Length: 20"), -1, "Content-length is correct"); + isnot(postData.postData.text + .indexOf("name=foo+bar&age=144"), -1, "Form data is correct"); + is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0, + "Response body's beginning is okay"); + + yield closeTabAndToolbox(); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js new file mode 100644 index 000000000..c6fa12401 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js @@ -0,0 +1,44 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that the page's resources are displayed in the console as they're +// loaded + +"use strict"; + +const TEST_NETWORK_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-network.html" + "?_date=" + + Date.now(); + +add_task(function* () { + yield loadTab("data:text/html;charset=utf-8,Web Console basic network " + + "logging test"); + let hud = yield openConsole(); + + yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_NETWORK_URI); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "running network console", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "test-network.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "testscript.js", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "test-image.png", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js new file mode 100644 index 000000000..b44b49453 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js @@ -0,0 +1,30 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that network log messages bring up the network panel. + +"use strict"; + +const TEST_NETWORK_REQUEST_URI = + "http://example.com/browser/devtools/client/webconsole/test/" + + "test-network-request.html"; + +add_task(function* () { + let finishedRequest = waitForFinishedRequest(({ request }) => { + return request.url.endsWith("test-network-request.html"); + }); + + const hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI); + let request = yield finishedRequest; + + yield hud.ui.openNetworkPanel(request.actor); + let toolbox = gDevTools.getToolbox(hud.target); + is(toolbox.currentToolId, "netmonitor", "Network panel was opened"); + let panel = toolbox.getCurrentPanel(); + let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem; + is(selected.attachment.method, request.request.method, + "The correct request is selected"); + is(selected.attachment.url, request.request.url, + "The correct request is definitely selected"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js new file mode 100644 index 000000000..265bc7c00 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js @@ -0,0 +1,95 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that network log messages bring up the network panel and select the +// right request even if it was previously filtered off. + +"use strict"; + +const TEST_FILE_URI = + "http://example.com/browser/devtools/client/webconsole/test/" + + "test-network.html"; +const TEST_URI = "data:text/html;charset=utf8,<p>test file URI"; + +var hud; + +add_task(function* () { + let Actions = require("devtools/client/netmonitor/actions/index"); + + let requests = []; + let { browser } = yield loadTab(TEST_URI); + + yield pushPrefEnv(); + hud = yield openConsole(); + hud.jsterm.clearOutput(); + + HUDService.lastFinishedRequest.callback = request => requests.push(request); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_FILE_URI); + yield loaded; + + yield testMessages(); + let htmlRequest = requests.find(e => e.request.url.endsWith("html")); + ok(htmlRequest, "htmlRequest was a html"); + + yield hud.ui.openNetworkPanel(htmlRequest.actor); + let toolbox = gDevTools.getToolbox(hud.target); + is(toolbox.currentToolId, "netmonitor", "Network panel was opened"); + + let panel = toolbox.getCurrentPanel(); + let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem; + is(selected.attachment.method, htmlRequest.request.method, + "The correct request is selected"); + is(selected.attachment.url, htmlRequest.request.url, + "The correct request is definitely selected"); + + // Filter out the HTML request. + panel.panelWin.gStore.dispatch(Actions.toggleFilterType("js")); + + yield toolbox.selectTool("webconsole"); + is(toolbox.currentToolId, "webconsole", "Web console was selected"); + yield hud.ui.openNetworkPanel(htmlRequest.actor); + + panel.panelWin.NetMonitorView.RequestsMenu.selectedItem; + is(selected.attachment.method, htmlRequest.request.method, + "The correct request is selected"); + is(selected.attachment.url, htmlRequest.request.url, + "The correct request is definitely selected"); + + // All tests are done. Shutdown. + HUDService.lastFinishedRequest.callback = null; + htmlRequest = browser = requests = hud = null; +}); + +function testMessages() { + return waitForMessages({ + webconsole: hud, + messages: [{ + text: "running network console logging tests", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "test-network.html", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }, + { + text: "testscript.js", + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); +} + +function pushPrefEnv() { + let deferred = promise.defer(); + let options = { + set: [["devtools.webconsole.filter.networkinfo", true]] + }; + SpecialPowers.pushPrefEnv(options, deferred.resolve); + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_notifications.js b/devtools/client/webconsole/test/browser_webconsole_notifications.js new file mode 100644 index 000000000..4bda9192f --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_notifications.js @@ -0,0 +1,77 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " + + "notifications"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let consoleOpened = promise.defer(); + let gotEvents = waitForEvents(consoleOpened.promise); + yield openConsole().then(() => { + consoleOpened.resolve(); + }); + + yield gotEvents; +}); + +function waitForEvents(onConsoleOpened) { + let deferred = promise.defer(); + + function webConsoleCreated(id) { + Services.obs.removeObserver(observer, "web-console-created"); + ok(HUDService.getHudReferenceById(id), "We have a hud reference"); + content.wrappedJSObject.console.log("adding a log message"); + } + + function webConsoleDestroyed(id) { + Services.obs.removeObserver(observer, "web-console-destroyed"); + ok(!HUDService.getHudReferenceById(id), "We do not have a hud reference"); + executeSoon(deferred.resolve); + } + + function webConsoleMessage(id, nodeID) { + Services.obs.removeObserver(observer, "web-console-message-created"); + ok(id, "we have a console ID"); + is(typeof nodeID, "string", "message node id is a string"); + onConsoleOpened.then(closeConsole); + } + + let observer = { + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), + + observe: function observe(subject, topic, data) { + subject = subject.QueryInterface(Ci.nsISupportsString); + + switch (topic) { + case "web-console-created": + webConsoleCreated(subject.data); + break; + case "web-console-destroyed": + webConsoleDestroyed(subject.data); + break; + case "web-console-message-created": + webConsoleMessage(subject, data); + break; + default: + break; + } + }, + + init: function init() { + Services.obs.addObserver(this, "web-console-created", false); + Services.obs.addObserver(this, "web-console-destroyed", false); + Services.obs.addObserver(this, "web-console-message-created", false); + } + }; + + observer.init(); + + return deferred.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js b/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js new file mode 100644 index 000000000..ae11305de --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that if a link without an onclick callback is clicked the link is +// opened in a new tab and no exception occurs (bug 999236). + +"use strict"; + +function test() { + function* runner() { + const TEST_EVAL_STRING = "document"; + const TEST_PAGE_URI = "http://example.com/browser/devtools/client/" + + "webconsole/test/test-console.html"; + const {tab} = yield loadTab(TEST_PAGE_URI); + const hud = yield openConsole(tab); + + hud.jsterm.execute(TEST_EVAL_STRING); + + const EXPECTED_OUTPUT = new RegExp("HTMLDocument \.+"); + + let messages = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "JS eval output", + text: EXPECTED_OUTPUT, + category: CATEGORY_OUTPUT, + }], + }); + + let messageNode = messages[0].matched.values().next().value; + + // The correct anchor is second in the message node; the first anchor has + // class .cm-variable. Ignore the first one by not matching anchors that + // have the class .cm-variable. + let urlNode = messageNode.querySelector("a:not(.cm-variable)"); + + let linkOpened = false; + let oldOpenUILinkIn = window.openUILinkIn; + window.openUILinkIn = function (aLink) { + if (aLink == TEST_PAGE_URI) { + linkOpened = true; + } + }; + + EventUtils.synthesizeMouseAtCenter(urlNode, {}, hud.iframeWindow); + + ok(linkOpened, "Clicking the URL opens the desired page"); + window.openUILinkIn = oldOpenUILinkIn; + } + + Task.spawn(runner).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_01.js b/devtools/client/webconsole/test/browser_webconsole_output_01.js new file mode 100644 index 000000000..c75577ea7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_01.js @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Whitelisting this test. +// As part of bug 1077403, the leaking uncaught rejection should be fixed. +// + +"use strict"; + +thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null"); + +// Test the webconsole output for various types of objects. + +const TEST_URI = "data:text/html;charset=utf8,test for console output - 01"; + +var {DebuggerServer} = require("devtools/server/main"); + +var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a"); +var initialString = longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH); + +var inputTests = [ + // 0 + { + input: "'hello \\nfrom \\rthe \\\"string world!'", + output: "\"hello \nfrom \rthe \"string world!\"", + consoleOutput: "hello \nfrom \rthe \"string world!", + }, + + // 1 + { + // unicode test + input: "'\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165'", + output: "\"\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165\"", + consoleOutput: "\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165", + }, + + // 2 + { + input: "'" + longString + "'", + output: '"' + initialString + "\"[\u2026]", + consoleOutput: initialString + "[\u2026]", + printOutput: initialString, + }, + + // 3 + { + input: "''", + output: '""', + consoleOutput: "", + printOutput: '""', + }, + + // 4 + { + input: "0", + output: "0", + }, + + // 5 + { + input: "'0'", + output: '"0"', + consoleOutput: "0", + }, + + // 6 + { + input: "42", + output: "42", + }, + + // 7 + { + input: "'42'", + output: '"42"', + consoleOutput: "42", + }, + + // 8 + { + input: "/foobar/", + output: "/foobar/", + inspectable: true, + }, + + // 9 + { + input: "Symbol()", + output: "Symbol()" + }, + + // 10 + { + input: "Symbol('foo')", + output: "Symbol(foo)" + }, + + // 11 + { + input: "Symbol.iterator", + output: "Symbol(Symbol.iterator)" + }, +]; + +longString = initialString = null; + +function test() { + requestLongerTimeout(2); + + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + return checkOutputForInputs(hud, inputTests); + }).then(finishUp); +} + +function finishUp() { + longString = initialString = inputTests = null; + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_02.js b/devtools/client/webconsole/test/browser_webconsole_output_02.js new file mode 100644 index 000000000..8018669a9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_02.js @@ -0,0 +1,183 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the webconsole output for various types of objects. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-02.html"; + +var inputTests = [ + // 0 - native named function + { + input: "document.getElementById", + output: "function getElementById()", + printOutput: "function getElementById() {\n [native code]\n}", + inspectable: true, + variablesViewLabel: "getElementById()", + }, + + // 1 - anonymous function + { + input: "(function() { return 42; })", + output: "function ()", + printOutput: "function () { return 42; }", + suppressClick: true + }, + + // 2 - named function + { + input: "window.testfn1", + output: "function testfn1()", + printOutput: "function testfn1() { return 42; }", + suppressClick: true + }, + + // 3 - anonymous function, but spidermonkey gives us an inferred name. + { + input: "testobj1.testfn2", + output: "function testobj1.testfn2()", + printOutput: "function () { return 42; }", + suppressClick: true + }, + + // 4 - named function with custom display name + { + input: "window.testfn3", + output: "function testfn3DisplayName()", + printOutput: "function testfn3() { return 42; }", + suppressClick: true + }, + + // 5 - basic array + { + input: "window.array1", + output: 'Array [ 1, 2, 3, "a", "b", "c", "4", "5" ]', + printOutput: "1,2,3,a,b,c,4,5", + inspectable: true, + variablesViewLabel: "Array[8]", + }, + + // 6 - array with objects + { + input: "window.array2", + output: 'Array [ "a", HTMLDocument \u2192 test-console-output-02.html, ' + + "<body>, DOMStringMap[0], DOMTokenList[0] ]", + printOutput: '"a,[object HTMLDocument],[object HTMLBodyElement],' + + '[object DOMStringMap],"', + inspectable: true, + variablesViewLabel: "Array[5]", + }, + + // 7 - array with more than 10 elements + { + input: "window.array3", + output: "Array [ 1, Window \u2192 test-console-output-02.html, null, " + + '"a", "b", undefined, false, "", -Infinity, ' + + "testfn3DisplayName(), 3 more\u2026 ]", + printOutput: '"1,[object Window],,a,b,,false,,-Infinity,' + + 'function testfn3() { return 42; },[object Object],foo,bar"', + inspectable: true, + variablesViewLabel: "Array[13]", + }, + + // 8 - array with holes and a cyclic reference + { + input: "window.array4", + output: 'Array [ <5 empty slots>, "test", Array[7] ]', + printOutput: '",,,,,test,"', + inspectable: true, + variablesViewLabel: "Array[7]", + }, + + // 9 + { + input: "window.typedarray1", + output: "Int32Array [ 1, 287, 8651, 40983, 8754 ]", + printOutput: "1,287,8651,40983,8754", + inspectable: true, + variablesViewLabel: "Int32Array[5]", + }, + + // 10 - Set with cyclic reference + { + input: "window.set1", + output: 'Set [ 1, 2, null, Array[13], "a", "b", undefined, <head>, ' + + "Set[9] ]", + printOutput: "[object Set]", + inspectable: true, + variablesViewLabel: "Set[9]", + }, + + // 11 - Object with cyclic reference and a getter + { + input: "window.testobj2", + output: 'Object { a: "b", c: "d", e: 1, f: "2", foo: Object, ' + + "bar: Object, getterTest: Getter }", + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 12 - Object with more than 10 properties + { + input: "window.testobj3", + output: 'Object { a: "b", c: "d", e: 1, f: "2", g: true, h: null, ' + + 'i: undefined, j: "", k: StyleSheetList[0], l: NodeList[5], ' + + "2 more\u2026 }", + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 13 - Object with a non-enumerable property that we do not show + { + input: "window.testobj4", + output: 'Object { a: "b", c: "d", 1 more\u2026 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 14 - Map with cyclic references + { + input: "window.map1", + output: 'Map { a: "b", HTMLCollection[2]: Object, Map[3]: Set[9] }', + printOutput: "[object Map]", + inspectable: true, + variablesViewLabel: "Map[3]", + }, + + // 15 - WeakSet + { + input: "window.weakset", + // Need a regexp because the order may vary. + output: new RegExp("WeakSet \\[ (String, <head>|<head>, String) \\]"), + printOutput: "[object WeakSet]", + inspectable: true, + variablesViewLabel: "WeakSet[2]", + }, + + // 16 - WeakMap + { + input: "window.weakmap", + // Need a regexp because the order may vary. + output: new RegExp("WeakMap { (String: 23, HTMLCollection\\[2\\]: Object|HTMLCollection\\[2\\]: Object, String: 23) }"), + printOutput: "[object WeakMap]", + inspectable: true, + variablesViewLabel: "WeakMap[2]", + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + yield checkOutputForInputs(hud, inputTests); + inputTests = null; + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_03.js b/devtools/client/webconsole/test/browser_webconsole_output_03.js new file mode 100644 index 000000000..bd77c2a4d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_03.js @@ -0,0 +1,168 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the webconsole output for various types of objects. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-03.html"; + +var inputTests = [ + + // 0 + { + input: "document", + output: "HTMLDocument \u2192 " + TEST_URI, + printOutput: "[object HTMLDocument]", + inspectable: true, + noClick: true, + }, + + // 1 + { + input: "window", + output: "Window \u2192 " + TEST_URI, + printOutput: "[object Window", + inspectable: true, + noClick: true, + }, + + // 2 + { + input: "document.body", + output: "<body>", + printOutput: "[object HTMLBodyElement]", + inspectable: true, + noClick: true, + }, + + // 3 + { + input: "document.body.dataset", + output: "DOMStringMap { }", + printOutput: "[object DOMStringMap]", + inspectable: true, + variablesViewLabel: "DOMStringMap[0]", + }, + + // 4 + { + input: "document.body.classList", + output: "DOMTokenList [ ]", + printOutput: '""', + inspectable: true, + variablesViewLabel: "DOMTokenList[0]", + }, + + // 5 + { + input: "window.location.href", + output: '"' + TEST_URI + '"', + noClick: true, + }, + + // 6 + { + input: "window.location", + output: "Location \u2192 " + TEST_URI, + printOutput: TEST_URI, + inspectable: true, + variablesViewLabel: "Location \u2192 test-console-output-03.html", + }, + + // 7 + { + input: "document.body.attributes", + output: "NamedNodeMap [ ]", + printOutput: "[object NamedNodeMap]", + inspectable: true, + variablesViewLabel: "NamedNodeMap[0]", + }, + + // 8 + { + input: "document.styleSheets", + output: "StyleSheetList [ ]", + printOutput: "[object StyleSheetList", + inspectable: true, + variablesViewLabel: "StyleSheetList[0]", + }, + + // 9 + { + input: "testBodyClassName()", + output: '<body class="test1 tezt2">', + printOutput: "[object HTMLBodyElement]", + inspectable: true, + noClick: true, + }, + + // 10 + { + input: "testBodyID()", + output: '<body class="test1 tezt2" id="foobarid">', + printOutput: "[object HTMLBodyElement]", + inspectable: true, + noClick: true, + }, + + // 11 + { + input: "document.body.classList", + output: 'DOMTokenList [ "test1", "tezt2" ]', + printOutput: '"test1 tezt2"', + inspectable: true, + variablesViewLabel: "DOMTokenList[2]", + }, + + // 12 + { + input: "testBodyDataset()", + output: '<body class="test1 tezt2" id="foobarid"' + + ' data-preview="zuzu"<a>foo">', + printOutput: "[object HTMLBodyElement]", + inspectable: true, + noClick: true, + }, + + // 13 + { + input: "document.body.dataset", + output: 'DOMStringMap { preview: "zuzu"<a>foo" }', + printOutput: "[object DOMStringMap]", + inspectable: true, + variablesViewLabel: "DOMStringMap[1]", + }, + + // 14 + { + input: "document.body.attributes", + output: 'NamedNodeMap [ class="test1 tezt2", id="foobarid", ' + + 'data-preview="zuzu"<a>foo" ]', + printOutput: "[object NamedNodeMap]", + inspectable: true, + variablesViewLabel: "NamedNodeMap[3]", + }, + + // 15 + { + input: "document.body.attributes[0]", + output: 'class="test1 tezt2"', + printOutput: "[object Attr]", + inspectable: true, + variablesViewLabel: 'class="test1 tezt2"', + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + yield checkOutputForInputs(hud, inputTests); + inputTests = null; + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_04.js b/devtools/client/webconsole/test/browser_webconsole_output_04.js new file mode 100644 index 000000000..d829594a7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_04.js @@ -0,0 +1,129 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Whitelisting this test. +// As part of bug 1077403, the leaking uncaught rejection should be fixed. +// + +"use strict"; + +thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null"); + +// Test the webconsole output for various types of objects. + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-04.html"; + +var inputTests = [ + // 0 + { + input: "testTextNode()", + output: '#text "hello world!"', + printOutput: "[object Text]", + inspectable: true, + noClick: true, + }, + + // 1 + { + input: "testCommentNode()", + output: /<!--\s+- Any copyright /, + printOutput: "[object Comment]", + inspectable: true, + noClick: true, + }, + + // 2 + { + input: "testDocumentFragment()", + output: "DocumentFragment [ <div#foo1.bar>, <div#foo3> ]", + printOutput: "[object DocumentFragment]", + inspectable: true, + variablesViewLabel: "DocumentFragment[2]", + }, + + // 3 + { + input: "testError()", + output: "TypeError: window.foobar is not a function\n" + + "Stack trace:\n" + + "testError@" + TEST_URI + ":44", + printOutput: '"TypeError: window.foobar is not a function"', + inspectable: true, + variablesViewLabel: "TypeError", + }, + + // 4 + { + input: "testDOMException()", + output: `DOMException [SyntaxError: "'foo;()bar!' is not a valid selector"`, + printOutput: `"SyntaxError: 'foo;()bar!' is not a valid selector"`, + inspectable: true, + variablesViewLabel: "SyntaxError", + }, + + // 5 + { + input: "testCSSStyleDeclaration()", + output: 'CSS2Properties { color: "green", font-size: "2em" }', + printOutput: "[object CSS2Properties]", + inspectable: true, + noClick: true, + }, + + // 6 + { + input: "testStyleSheetList()", + output: "StyleSheetList [ CSSStyleSheet ]", + printOutput: "[object StyleSheetList", + inspectable: true, + variablesViewLabel: "StyleSheetList[1]", + }, + + // 7 + { + input: "document.styleSheets[0]", + output: "CSSStyleSheet", + printOutput: "[object CSSStyleSheet]", + inspectable: true, + }, + + // 8 + { + input: "document.styleSheets[0].cssRules", + output: "CSSRuleList [ CSSStyleRule, CSSMediaRule ]", + printOutput: "[object CSSRuleList", + inspectable: true, + variablesViewLabel: "CSSRuleList[2]", + }, + + // 9 + { + input: "document.styleSheets[0].cssRules[0]", + output: 'CSSStyleRule "p, div"', + printOutput: "[object CSSStyleRule", + inspectable: true, + variablesViewLabel: "CSSStyleRule", + }, + + // 10 + { + input: "document.styleSheets[0].cssRules[1]", + output: 'CSSMediaRule "print"', + printOutput: "[object CSSMediaRule", + inspectable: true, + variablesViewLabel: "CSSMediaRule", + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + const {tab} = yield loadTab(TEST_URI); + const hud = yield openConsole(tab); + yield checkOutputForInputs(hud, inputTests); + inputTests = null; + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_05.js b/devtools/client/webconsole/test/browser_webconsole_output_05.js new file mode 100644 index 000000000..53bfd768c --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_05.js @@ -0,0 +1,177 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the webconsole output for various types of objects. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,test for console output - 05"; +const {ELLIPSIS} = require("devtools/shared/l10n"); + +// March, 1960: The first implementation of Lisp. From Wikipedia: +// +// > Lisp was first implemented by Steve Russell on an IBM 704 computer. Russell +// > had read McCarthy's paper, and realized (to McCarthy's surprise) that the +// > Lisp eval function could be implemented in machine code. The result was a +// > working Lisp interpreter which could be used to run Lisp programs, or more +// > properly, 'evaluate Lisp expressions.' +var testDate = -310435200000; + +var inputTests = [ + // 0 + { + input: "/foo?b*\\s\"ar/igym", + output: "/foo?b*\\s\"ar/gimy", + printOutput: "/foo?b*\\s\"ar/gimy", + inspectable: true, + }, + + // 1 + { + input: "null", + output: "null", + }, + + // 2 + { + input: "undefined", + output: "undefined", + }, + + // 3 + { + input: "true", + output: "true", + }, + + // 4 + { + input: "new Boolean(false)", + output: "Boolean { false }", + printOutput: "false", + inspectable: true, + variablesViewLabel: "Boolean { false }" + }, + + // 5 + { + input: "new Date(" + testDate + ")", + output: "Date " + (new Date(testDate)).toISOString(), + printOutput: (new Date(testDate)).toString(), + inspectable: true, + }, + + // 6 + { + input: "new Date('test')", + output: "Invalid Date", + printOutput: "Invalid Date", + inspectable: true, + variablesViewLabel: "Invalid Date", + }, + + // 7 + { + input: "Date.prototype", + output: /Object \{.*\}/, + printOutput: "Invalid Date", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 8 + { + input: "new Number(43)", + output: "Number { 43 }", + printOutput: "43", + inspectable: true, + variablesViewLabel: "Number { 43 }" + }, + + // 9 + { + input: "new String('hello')", + output: /String { "hello", 6 more.* }/, + printOutput: "hello", + inspectable: true, + variablesViewLabel: "String" + }, + + // 10 + { + input: "(function () { var s = new String('hello'); s.whatever = 23; " + + " return s;})()", + output: /String { "hello", whatever: 23, 6 more.* }/, + printOutput: "hello", + inspectable: true, + variablesViewLabel: "String" + }, + + // 11 + { + input: "(function () { var s = new String('hello'); s[8] = 'x'; " + + " return s;})()", + output: /String { "hello", 8: "x", 6 more.* }/, + printOutput: "hello", + inspectable: true, + variablesViewLabel: "String" + }, + + // 12 + { + // XXX: Can't test fulfilled and rejected promises, because promises get + // settled on the next tick of the event loop. + input: "new Promise(function () {})", + output: 'Promise { <state>: "pending" }', + printOutput: "[object Promise]", + inspectable: true, + variablesViewLabel: "Promise" + }, + + // 13 + { + input: "(function () { var p = new Promise(function () {}); " + + "p.foo = 1; return p; }())", + output: 'Promise { <state>: "pending", foo: 1 }', + printOutput: "[object Promise]", + inspectable: true, + variablesViewLabel: "Promise" + }, + + // 14 + { + input: "new Object({1: 'this\\nis\\nsupposed\\nto\\nbe\\na\\nvery" + + "\\nlong\\nstring\\n,shown\\non\\na\\nsingle\\nline', " + + "2: 'a shorter string', 3: 100})", + output: '[ <1 empty slot>, "this is supposed to be a very long ' + ELLIPSIS + + '", "a shorter string", 100 ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[4]" + }, + + // 15 + { + input: "new Proxy({a:1},[1,2,3])", + output: 'Proxy { <target>: Object, <handler>: Array[3] }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Proxy" + } +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + return checkOutputForInputs(hud, inputTests); + }).then(finishUp); +} + +function finishUp() { + inputTests = testDate = null; + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_06.js b/devtools/client/webconsole/test/browser_webconsole_output_06.js new file mode 100644 index 000000000..ad69b3908 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js @@ -0,0 +1,283 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the webconsole output for various arrays. + +const TEST_URI = "data:text/html;charset=utf8,test for console output - 06"; +const {ELLIPSIS} = require("devtools/shared/l10n"); + +const testStrIn = "SHOW\\nALL\\nOF\\nTHIS\\nON\\nA\\nSINGLE" + + "\\nLINE ONLY. ESCAPE ALL NEWLINE"; +const testStrOut = "SHOW ALL OF THIS ON A SINGLE LINE O" + ELLIPSIS; + +var inputTests = [ + // 1 - array with empty slots only + { + input: "Array(5)", + output: "Array [ <5 empty slots> ]", + printOutput: ",,,,", + inspectable: true, + variablesViewLabel: "Array[5]", + }, + // 2 - array with one empty slot at the beginning + { + input: "[,1,2,3]", + output: "Array [ <1 empty slot>, 1, 2, 3 ]", + printOutput: ",1,2,3", + inspectable: true, + variablesViewLabel: "Array[4]", + }, + // 3 - array with multiple consecutive empty slots at the beginning + { + input: "[,,,3,4,5]", + output: "Array [ <3 empty slots>, 3, 4, 5 ]", + printOutput: ",,,3,4,5", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + // 4 - array with one empty slot at the middle + { + input: "[0,1,,3,4,5]", + output: "Array [ 0, 1, <1 empty slot>, 3, 4, 5 ]", + printOutput: "0,1,,3,4,5", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + // 5 - array with multiple successive empty slots at the middle + { + input: "[0,1,,,,5]", + output: "Array [ 0, 1, <3 empty slots>, 5 ]", + printOutput: "0,1,,,,5", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + // 6 - array with multiple non successive single empty slots + { + input: "[0,,2,,4,5]", + output: "Array [ 0, <1 empty slot>, 2, <1 empty slot>, 4, 5 ]", + printOutput: "0,,2,,4,5", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + // 7 - array with multiple multi-slot holes + { + input: "[0,,,3,,,,7,8]", + output: "Array [ 0, <2 empty slots>, 3, <3 empty slots>, 7, 8 ]", + printOutput: "0,,,3,,,,7,8", + inspectable: true, + variablesViewLabel: "Array[9]", + }, + // 8 - array with a single slot hole at the end + { + input: "[0,1,2,3,4,,]", + output: "Array [ 0, 1, 2, 3, 4, <1 empty slot> ]", + printOutput: "0,1,2,3,4,", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + // 9 - array with multiple consecutive empty slots at the end + { + input: "[0,1,2,,,,]", + output: "Array [ 0, 1, 2, <3 empty slots> ]", + printOutput: "0,1,2,,,", + inspectable: true, + variablesViewLabel: "Array[6]", + }, + + // 10 - array with members explicitly set to null + { + input: "[0,null,null,3,4,5]", + output: "Array [ 0, null, null, 3, 4, 5 ]", + printOutput: "0,,,3,4,5", + inspectable: true, + variablesViewLabel: "Array[6]" + }, + + // 11 - array with members explicitly set to undefined + { + input: "[0,undefined,undefined,3,4,5]", + output: "Array [ 0, undefined, undefined, 3, 4, 5 ]", + printOutput: "0,,,3,4,5", + inspectable: true, + variablesViewLabel: "Array[6]" + }, + + // 12 - array with long strings as elements + { + input: '["' + testStrIn + '", "' + testStrIn + '", "' + testStrIn + '"]', + output: 'Array [ "' + testStrOut + '", "' + testStrOut + '", "' + + testStrOut + '" ]', + inspectable: true, + printOutput: "SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE " + + "ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. " + + "ESCAPE ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\n" + + "LINE ONLY. ESCAPE ALL NEWLINE", + variablesViewLabel: "Array[3]" + }, + + // 13 + { + input: '({0: "a", 1: "b"})', + output: 'Object [ "a", "b" ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[2]", + }, + + // 14 + { + input: '({0: "a", 42: "b"})', + output: '[ "a", <9 empty slots>, 33 more\u2026 ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[43]", + }, + + // 15 + { + input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' + + '7: "h", 8: "i", 9: "j", 10: "k", 11: "l"})', + output: 'Object [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", ' + + "2 more\u2026 ]", + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[12]", + }, + + // 16 + { + input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' + + '7: "h", 8: "i", 9: "j", 10: "k", 11: "l", m: "n"})', + output: 'Object { 0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", ' + + '6: "g", 7: "h", 8: "i", 9: "j", 3 more\u2026 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 17 + { + input: '({" ": "a"})', + output: 'Object { : "a" }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 18 + { + input: '({})', + output: 'Object { }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 19 + { + input: '({length: 0})', + output: 'Object [ ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[0]", + }, + + // 20 + { + input: '({length: 1})', + output: '[ <1 empty slot> ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[1]", + }, + + // 21 + { + input: '({0: "a", 1: "b", length: 1})', + output: 'Object { 0: "a", 1: "b", length: 1 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 22 + { + input: '({0: "a", 1: "b", length: 2})', + output: 'Object [ "a", "b" ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[2]", + }, + + // 23 + { + input: '({0: "a", 1: "b", length: 3})', + output: '[ "a", "b", <1 empty slot> ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[3]", + }, + + // 24 + { + input: '({0: "a", 2: "b", length: 2})', + output: 'Object { 0: "a", 2: "b", length: 2 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 25 + { + input: '({0: "a", 2: "b", length: 3})', + output: '[ "a", <1 empty slot>, "b" ]', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object[3]", + }, + + // 26 + { + input: '({0: "a", b: "b", length: 1})', + output: 'Object { 0: "a", b: "b", length: 1 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 27 + { + input: '({0: "a", b: "b", length: 2})', + output: 'Object { 0: "a", b: "b", length: 2 }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, + + // 28 + { + input: '({42: "a"})', + output: 'Object { 42: "a" }', + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object", + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + return checkOutputForInputs(hud, inputTests); + }).then(finishUp); +} + +function finishUp() { + inputTests = null; + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js new file mode 100644 index 000000000..22de843f9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js @@ -0,0 +1,72 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that multiple messages are copied into the clipboard and that they are +// separated by new lines. See bug 916997. + +"use strict"; + +add_task(function* () { + const TEST_URI = "data:text/html;charset=utf8,<p>hello world, bug 916997"; + let clipboardValue = ""; + + yield loadTab(TEST_URI); + let hud = yield openConsole(); + hud.jsterm.clearOutput(); + + let controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled"); + + content.console.log("Hello world! bug916997a"); + content.console.log("Hello world 2! bug916997b"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Hello world! bug916997a", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, { + text: "Hello world 2! bug916997b", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + hud.ui.output.selectAllMessages(); + hud.outputNode.focus(); + + goUpdateCommand("cmd_copy"); + controller = top.document.commandDispatcher + .getControllerForCommand("cmd_copy"); + is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); + + let selection = hud.iframeWindow.getSelection() + ""; + info("selection '" + selection + "'"); + + waitForClipboard((str) => { + clipboardValue = str; + return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1; + }, + () => { + goDoCommand("cmd_copy"); + }, + () => { + info("clipboard value '" + clipboardValue + "'"); + let lines = clipboardValue.trim().split("\n"); + is(hud.outputNode.children.length, 2, "number of messages"); + is(lines.length, hud.outputNode.children.length, "number of lines"); + isnot(lines[0].indexOf("bug916997a"), -1, + "first message text includes 'bug916997a'"); + isnot(lines[1].indexOf("bug916997b"), -1, + "second message text includes 'bug916997b'"); + is(lines[0].indexOf("bug916997b"), -1, + "first message text does not include 'bug916997b'"); + }, + () => { + info("last clipboard value: '" + clipboardValue + "'"); + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js new file mode 100644 index 000000000..097eb3b37 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Whitelisting this test. +// As part of bug 1077403, the leaking uncaught rejections should be fixed. + +"use strict"; + +thisTestLeaksUncaughtRejectionsAndShouldBeFixed(null); +thisTestLeaksUncaughtRejectionsAndShouldBeFixed( + "TypeError: this.toolbox is null"); + +// Test the webconsole output for various types of DOM Nodes. + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-dom-elements.html"; + +var inputTests = [ + { + input: "testBodyNode()", + output: '<body class="body-class" id="body-id">', + printOutput: "[object HTMLBodyElement]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testDocumentElement()", + output: '<html dir="ltr" lang="en-US">', + printOutput: "[object HTMLHtmlElement]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testDocument()", + output: "HTMLDocument \u2192 " + TEST_URI, + printOutput: "[object HTMLDocument]", + inspectable: true, + noClick: true, + inspectorIcon: false + }, + + { + input: "testNode()", + output: '<p some-attribute="some-value">', + printOutput: "[object HTMLParagraphElement]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testNodeList()", + output: "NodeList [ <p>, <p#lots-of-attributes>, <iframe>, " + + "<div.some.classname.here.with.more.classnames.here>, " + + "<svg>, <clipPath>, <rect>, <script> ]", + printOutput: "[object NodeList]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testNodeInIframe()", + output: "<p>", + printOutput: "[object HTMLParagraphElement]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testLotsOfAttributes()", + output: '<p id="lots-of-attributes" a="" b="" c="" d="" e="" f="" g="" ' + + 'h="" i="" j="" k="" l="" m="" n="">', + printOutput: "[object HTMLParagraphElement]", + inspectable: true, + noClick: true, + inspectorIcon: true + }, + + { + input: "testDocumentFragment()", + output: "DocumentFragment [ <span.foo>, <div#fragdiv> ]", + printOutput: "[object DocumentFragment]", + inspectable: true, + noClick: true, + inspectorIcon: false + }, + + { + input: "testNodeInDocumentFragment()", + output: '<span class="foo" data-lolz="hehe">', + printOutput: "[object HTMLSpanElement]", + inspectable: true, + noClick: true, + inspectorIcon: false + }, + + { + input: "testUnattachedNode()", + output: '<p class="such-class" data-data="such-data">', + printOutput: "[object HTMLParagraphElement]", + inspectable: true, + noClick: true, + inspectorIcon: false + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + yield checkOutputForInputs(hud, inputTests); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js new file mode 100644 index 000000000..51fe89e01 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the inspector links in the webconsole output for DOM Nodes actually +// open the inspector and select the right node. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-dom-elements.html"; + +const TEST_DATA = [ + { + // The first test shouldn't be returning the body element as this is the + // default selected node, so re-selecting it won't fire the + // inspector-updated event + input: "testNode()", + output: '<p some-attribute="some-value">', + displayName: "p", + attrs: [{name: "some-attribute", value: "some-value"}] + }, + { + input: "testBodyNode()", + output: '<body class="body-class" id="body-id">', + displayName: "body", + attrs: [ + { + name: "class", value: "body-class" + }, + { + name: "id", value: "body-id" + } + ] + }, + { + input: "testNodeInIframe()", + output: "<p>", + displayName: "p", + attrs: [] + }, + { + input: "testDocumentElement()", + output: '<html dir="ltr" lang="en-US">', + displayName: "html", + attrs: [ + { + name: "dir", + value: "ltr" + }, + { + name: "lang", + value: "en-US" + } + ] + } +]; + +function test() { + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + yield checkDomElementHighlightingForInputs(hud, TEST_DATA); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js new file mode 100644 index 000000000..b5dd125d1 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js @@ -0,0 +1,70 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that inspector links in webconsole outputs for DOM Nodes highlight +// the actual DOM Nodes on hover + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-dom-elements.html"; + +function test() { + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + let toolbox = gDevTools.getToolbox(hud.target); + + // Loading the inspector panel at first, to make it possible to listen for + // new node selections + yield toolbox.loadTool("inspector"); + toolbox.getPanel("inspector"); + + info("Executing 'testNode()' in the web console to output a DOM Node"); + let [result] = yield jsEval("testNode()", hud, { + text: '<p some-attribute="some-value">' + }); + + let elementNodeWidget = yield getWidget(result); + + let nodeFront = yield hoverOverWidget(elementNodeWidget, toolbox); + let attrs = nodeFront.attributes; + is(nodeFront.tagName, "P", "The correct node was highlighted"); + is(attrs[0].name, "some-attribute", "The correct node was highlighted"); + is(attrs[0].value, "some-value", "The correct node was highlighted"); + }).then(finishTest); +} + +function jsEval(input, hud, message) { + hud.jsterm.execute(input); + return waitForMessages({ + webconsole: hud, + messages: [message] + }); +} + +function* getWidget(result) { + info("Getting the output ElementNode widget"); + + let msg = [...result.matched][0]; + let elementNodeWidget = [...msg._messageObject.widgets][0]; + ok(elementNodeWidget, "ElementNode widget found in the output"); + + info("Waiting for the ElementNode widget to be linked to the inspector"); + yield elementNodeWidget.linkToInspector(); + + return elementNodeWidget; +} + +function* hoverOverWidget(widget, toolbox) { + info("Hovering over the output to highlight the node"); + + let onHighlight = toolbox.once("node-highlight"); + EventUtils.sendMouseEvent({type: "mouseover"}, widget.element, + widget.element.ownerDocument.defaultView); + let nodeFront = yield onHighlight; + ok(true, "The highlighter was shown on a node"); + return nodeFront; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js new file mode 100644 index 000000000..c7eb94902 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js @@ -0,0 +1,113 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test that inspector links in the webconsole output for DOM Nodes do not try +// to highlight or select nodes once they have been detached + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-dom-elements.html"; + +const TEST_DATA = [ + { + // The first test shouldn't be returning the body element as this is the + // default selected node, so re-selecting it won't fire the + // inspector-updated event + input: "testNode()", + output: '<p some-attribute="some-value">' + }, + { + input: "testSvgNode()", + output: '<clipPath>' + }, + { + input: "testBodyNode()", + output: '<body class="body-class" id="body-id">' + }, + { + input: "testNodeInIframe()", + output: "<p>" + }, + { + input: "testDocumentElement()", + output: '<html dir="ltr" lang="en-US">' + } +]; + +const PREF = "devtools.webconsole.persistlog"; + +function test() { + Services.prefs.setBoolPref(PREF, true); + registerCleanupFunction(() => Services.prefs.clearUserPref(PREF)); + + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + let toolbox = gDevTools.getToolbox(hud.target); + + info("Executing the test data"); + let widgets = []; + for (let data of TEST_DATA) { + let [result] = yield jsEval(data.input, hud, {text: data.output}); + let {widget} = yield getWidgetAndMessage(result); + widgets.push(widget); + } + + info("Reloading the page"); + yield reloadPage(); + + info("Iterating over the ElementNode widgets"); + for (let widget of widgets) { + // Verify that openNodeInInspector rejects since the associated dom node + // doesn't exist anymore + yield widget.openNodeInInspector().then(() => { + ok(false, "The openNodeInInspector promise resolved"); + }, () => { + ok(true, "The openNodeInInspector promise rejected as expected"); + }); + yield toolbox.selectTool("webconsole"); + + // Verify that highlightDomNode rejects too, for the same reason + yield widget.highlightDomNode().then(() => { + ok(false, "The highlightDomNode promise resolved"); + }, () => { + ok(true, "The highlightDomNode promise rejected as expected"); + }); + } + }).then(finishTest); +} + +function jsEval(input, hud, message) { + info("Executing '" + input + "' in the web console"); + hud.jsterm.execute(input); + return waitForMessages({ + webconsole: hud, + messages: [message] + }); +} + +function* getWidgetAndMessage(result) { + info("Getting the output ElementNode widget"); + + let msg = [...result.matched][0]; + let widget = [...msg._messageObject.widgets][0]; + ok(widget, "ElementNode widget found in the output"); + + info("Waiting for the ElementNode widget to be linked to the inspector"); + yield widget.linkToInspector(); + + return {widget: widget, msg: msg}; +} + +function reloadPage() { + let def = promise.defer(); + gBrowser.selectedBrowser.addEventListener("load", function onload() { + gBrowser.selectedBrowser.removeEventListener("load", onload, true); + def.resolve(); + }, true); + content.location.reload(); + return def.promise; +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js new file mode 100644 index 000000000..9d35ef984 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test the inspector links in the webconsole output for namespaced elements +// actually open the inspector and select the right node. + +const XHTML = ` + <!DOCTYPE html> + <html xmlns="http://www.w3.org/1999/xhtml" + xmlns:svg="http://www.w3.org/2000/svg"> + <body> + <svg:svg width="100" height="100"> + <svg:clipPath id="clip"> + <svg:rect id="rectangle" x="0" y="0" width="10" height="5"></svg:rect> + </svg:clipPath> + <svg:circle cx="0" cy="0" r="5"></svg:circle> + </svg:svg> + </body> + </html> +`; + +const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML); + +const TEST_DATA = [ + { + input: 'document.querySelector("clipPath")', + output: '<svg:clipPath id="clip">', + displayName: "svg:clipPath" + }, + { + input: 'document.querySelector("circle")', + output: '<svg:circle cx="0" cy="0" r="5">', + displayName: "svg:circle" + }, +]; + +function test() { + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + yield checkDomElementHighlightingForInputs(hud, TEST_DATA); + }).then(finishTest); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_events.js b/devtools/client/webconsole/test/browser_webconsole_output_events.js new file mode 100644 index 000000000..9bd04bfc7 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_events.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Whitelisting this test. +// As part of bug 1077403, the leaking uncaught rejection should be fixed. + +"use strict"; + +thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null"); + +// Test the webconsole output for DOM events. + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-events.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + hud.jsterm.execute("testDOMEvents()"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "testDOMEvents() output", + text: "undefined", + category: CATEGORY_OUTPUT, + }], + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log() output for mousemove", + text: /eventLogger mousemove { target: .+, buttons: 0, clientX: \d+, clientY: \d+, layerX: \d+, layerY: \d+ }/, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log() output for keypress", + text: /eventLogger keypress Shift { target: .+, key: .+, charCode: \d+, keyCode: \d+ }/, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_output_order.js b/devtools/client/webconsole/test/browser_webconsole_output_order.js new file mode 100644 index 000000000..66fa74cb0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_order.js @@ -0,0 +1,47 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that any output created from calls to the console API comes before the +// echoed JavaScript. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console.html"; + +add_task(function* () { + yield loadTab(TEST_URI); + let hud = yield openConsole(); + + let jsterm = hud.jsterm; + + jsterm.clearOutput(); + jsterm.execute("console.log('foo', 'bar');"); + + let [functionCall, consoleMessage, result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "console.log('foo', 'bar');", + category: CATEGORY_INPUT, + }, + { + text: "foo bar", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }, + { + text: "undefined", + category: CATEGORY_OUTPUT, + }] + }); + + let fncallNode = [...functionCall.matched][0]; + let consoleMessageNode = [...consoleMessage.matched][0]; + let resultNode = [...result.matched][0]; + is(fncallNode.nextElementSibling, consoleMessageNode, + "console.log() is followed by 'foo' 'bar'"); + is(consoleMessageNode.nextElementSibling, resultNode, + "'foo' 'bar' is followed by undefined"); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_output_regexp.js b/devtools/client/webconsole/test/browser_webconsole_output_regexp.js new file mode 100644 index 000000000..2d6e767e9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_regexp.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Test the webconsole output for various types of objects. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-output-regexp.html"; + +var inputTests = [ + // 0 + { + input: "/foo/igym", + output: "/foo/gimy", + printOutput: "Error: source called", + inspectable: true, + }, +]; + +function test() { + requestLongerTimeout(2); + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + return checkOutputForInputs(hud, inputTests); + }).then(finishUp); +} + +function finishUp() { + inputTests = null; + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_output_table.js b/devtools/client/webconsole/test/browser_webconsole_output_table.js new file mode 100644 index 000000000..372afb28d --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_output_table.js @@ -0,0 +1,199 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that console.table() works as intended. + +"use strict"; + +const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + + "test/test-console-table.html"; + +const TEST_DATA = [ + { + command: "console.table(languages1)", + data: [ + { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" }, + { _index: 1, name: "Object", fileExtension: "\".ts\"" }, + { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" } + ], + columns: { _index: "(index)", name: "name", fileExtension: "fileExtension" } + }, + { + command: "console.table(languages1, 'name')", + data: [ + { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" }, + { _index: 1, name: "Object", fileExtension: "\".ts\"" }, + { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" } + ], + columns: { _index: "(index)", name: "name" } + }, + { + command: "console.table(languages1, ['name'])", + data: [ + { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" }, + { _index: 1, name: "Object", fileExtension: "\".ts\"" }, + { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" } + ], + columns: { _index: "(index)", name: "name" } + }, + { + command: "console.table(languages2)", + data: [ + { _index: "csharp", name: "\"C#\"", paradigm: "\"object-oriented\"" }, + { _index: "fsharp", name: "\"F#\"", paradigm: "\"functional\"" } + ], + columns: { _index: "(index)", name: "name", paradigm: "paradigm" } + }, + { + command: "console.table([[1, 2], [3, 4]])", + data: [ + { _index: 0, 0: "1", 1: "2" }, + { _index: 1, 0: "3", 1: "4" } + ], + columns: { _index: "(index)", 0: "0", 1: "1" } + }, + { + command: "console.table({a: [1, 2], b: [3, 4]})", + data: [ + { _index: "a", 0: "1", 1: "2" }, + { _index: "b", 0: "3", 1: "4" } + ], + columns: { _index: "(index)", 0: "0", 1: "1" } + }, + { + command: "console.table(family)", + data: [ + { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", + age: "32" }, + { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", + age: "33" }, + { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", + age: "5" }, + { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" }, + ], + columns: { _index: "(index)", firstName: "firstName", lastName: "lastName", + age: "age" } + }, + { + command: "console.table(family, [])", + data: [ + { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", + age: "32" }, + { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", + age: "33" }, + { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", + age: "5" }, + { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" }, + ], + columns: { _index: "(index)" } + }, + { + command: "console.table(family, ['firstName', 'lastName'])", + data: [ + { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"", + age: "32" }, + { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"", + age: "33" }, + { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"", + age: "5" }, + { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" }, + ], + columns: { _index: "(index)", firstName: "firstName", lastName: "lastName" } + }, + { + command: "console.table(mySet)", + data: [ + { _index: 0, _value: "1" }, + { _index: 1, _value: "5" }, + { _index: 2, _value: "\"some text\"" }, + { _index: 3, _value: "null" }, + { _index: 4, _value: "undefined" } + ], + columns: { _index: "(iteration index)", _value: "Values" } + }, + { + command: "console.table(myMap)", + data: [ + { _index: 0, _key: "\"a string\"", + _value: "\"value associated with 'a string'\"" }, + { _index: 1, _key: "5", _value: "\"value associated with 5\"" }, + ], + columns: { _index: "(iteration index)", _key: "Key", _value: "Values" } + }, + { + command: "console.table(weakset)", + data: [ + { _value: "String" }, + { _value: "String" }, + ], + columns: { _index: "(iteration index)", _value: "Values" }, + couldBeOutOfOrder: true, + }, + { + command: "console.table(weakmap)", + data: [ + { _key: "String", _value: "\"oh no\"" }, + { _key: "String", _value: "23" }, + ], + columns: { _index: "(iteration index)", _key: "Key", _value: "Values" }, + couldBeOutOfOrder: true, + }, +]; + +add_task(function* () { + const {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + + for (let testdata of TEST_DATA) { + hud.jsterm.clearOutput(); + + info("Executing " + testdata.command); + + let onTableRender = once(hud.ui, "messages-table-rendered"); + hud.jsterm.execute(testdata.command); + yield onTableRender; + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: testdata.command + " output", + consoleTable: true + }], + }); + + let node = [...result.matched][0]; + ok(node, "found trace log node"); + + let obj = node._messageObject; + ok(obj, "console.trace message object"); + + ok(obj._data, "found table data object"); + + let data = obj._data.map(entries => { + let entryResult = {}; + + for (let key of Object.keys(entries)) { + // If the results can be out of order, then ignore _index. + if (!testdata.couldBeOutOfOrder || key !== "_index") { + entryResult[key] = entries[key] instanceof HTMLElement ? + entries[key].textContent : entries[key]; + } + } + + return entryResult; + }); + + if (testdata.couldBeOutOfOrder) { + data = data.map(e => e.toSource()).sort().join(","); + let expected = testdata.data.map(e => e.toSource()).sort().join(","); + is(data, expected, "table data is correct"); + } else { + is(data.toSource(), testdata.data.toSource(), "table data is correct"); + } + ok(obj._columns, "found table column object"); + is(obj._columns.toSource(), testdata.columns.toSource(), + "table column is correct"); + } +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_promise.js b/devtools/client/webconsole/test/browser_webconsole_promise.js new file mode 100644 index 000000000..59cd287ca --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_promise.js @@ -0,0 +1,35 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Bug 1148759 - Test the webconsole can display promises inside objects. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,test for console and promises"; + +var inputTests = [ + // 0 + { + input: "({ x: Promise.resolve() })", + output: "Object { x: Promise }", + printOutput: "[object Object]", + inspectable: true, + variablesViewLabel: "Object" + }, +]; + +function test() { + requestLongerTimeout(2); + + Task.spawn(function* () { + let {tab} = yield loadTab(TEST_URI); + let hud = yield openConsole(tab); + return checkOutputForInputs(hud, inputTests); + }).then(finishUp); +} + +function finishUp() { + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_property_provider.js b/devtools/client/webconsole/test/browser_webconsole_property_provider.js new file mode 100644 index 000000000..0c9b4c4e3 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_property_provider.js @@ -0,0 +1,46 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests the property provider, which is part of the code completion +// infrastructure. + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test the JS property provider"; + +function test() { + loadTab(TEST_URI).then(testPropertyProvider); +} + +function testPropertyProvider({browser}) { + browser.removeEventListener("load", testPropertyProvider, true); + let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider"); + + let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {}); + tmp.addDebuggerToGlobal(tmp); + let dbg = new tmp.Debugger(); + let dbgWindow = dbg.addDebuggee(content); + + let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined"); + is(completion.matches.length, 0, "no match for 'thisIsNotDefined"); + + // This is a case the PropertyProvider can't handle. Should return null. + completion = JSPropertyProvider(dbgWindow, null, "window[1].acb"); + is(completion, null, "no match for 'window[1].acb"); + + // A very advanced completion case. + let strComplete = + "function a() { }document;document.getElementById(window.locatio"; + completion = JSPropertyProvider(dbgWindow, null, strComplete); + ok(completion.matches.length == 2, "two matches found"); + ok(completion.matchProp == "locatio", "matching part is 'test'"); + let matches = completion.matches; + matches.sort(); + ok(matches[0] == "location", "the first match is 'location'"); + ok(matches[1] == "locationbar", "the second match is 'locationbar'"); + + dbg.removeDebuggee(content); + finishTest(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_reflow.js b/devtools/client/webconsole/test/browser_webconsole_reflow.js new file mode 100644 index 000000000..86caa10e0 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_reflow.js @@ -0,0 +1,33 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " + + "reflow activity"; + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + function onReflowListenersReady() { + browser.contentDocument.body.style.display = "none"; + browser.contentDocument.body.clientTop; + } + + Services.prefs.setBoolPref("devtools.webconsole.filter.csslog", true); + hud.ui._updateReflowActivityListener(onReflowListenersReady); + Services.prefs.clearUserPref("devtools.webconsole.filter.csslog"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: /reflow: /, + category: CATEGORY_CSS, + severity: SEVERITY_LOG, + }], + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js new file mode 100644 index 000000000..566af8d42 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js @@ -0,0 +1,76 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,<p>test Scratchpad panel " + + "linking</p>"; + +var { Tools } = require("devtools/client/definitions"); +var { isTargetSupported } = Tools.scratchpad; + +function pushPrefEnv() { + let deferred = promise.defer(); + let options = {"set": + [["devtools.scratchpad.enabled", true] + ]}; + SpecialPowers.pushPrefEnv(options, deferred.resolve); + return deferred.promise; +} + +add_task(function* () { + waitForExplicitFinish(); + + yield pushPrefEnv(); + + yield loadTab(TEST_URI); + + info("Opening toolbox with Scratchpad panel"); + + let target = TargetFactory.forTab(gBrowser.selectedTab); + let toolbox = yield gDevTools.showToolbox(target, "scratchpad", "window"); + + let scratchpadPanel = toolbox.getPanel("scratchpad"); + let { scratchpad } = scratchpadPanel; + is(toolbox.getCurrentPanel(), scratchpadPanel, + "Scratchpad is currently selected panel"); + + info("Switching to webconsole panel"); + + let webconsolePanel = yield toolbox.selectTool("webconsole"); + let { hud } = webconsolePanel; + is(toolbox.getCurrentPanel(), webconsolePanel, + "Webconsole is currently selected panel"); + + info("console.log()ing from Scratchpad"); + + scratchpad.setText("console.log('foobar-from-scratchpad')"); + scratchpad.run(); + let messages = yield waitForMessages({ + webconsole: hud, + messages: [{ text: "foobar-from-scratchpad" }] + }); + + info("Clicking link to switch to and focus Scratchpad"); + + let [matched] = [...messages[0].matched]; + ok(matched, "Found logged message from Scratchpad"); + let anchor = matched.querySelector(".message-location .frame-link-filename"); + + toolbox.on("scratchpad-selected", function selected() { + toolbox.off("scratchpad-selected", selected); + + is(toolbox.getCurrentPanel(), scratchpadPanel, + "Clicking link switches to Scratchpad panel"); + + is(Services.ww.activeWindow, toolbox.win.parent, + "Scratchpad's toolbox is focused"); + + Tools.scratchpad.isTargetSupported = isTargetSupported; + finish(); + }); + + EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js new file mode 100644 index 000000000..779d80376 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure that [Learn More] links appear alongside any errors listed +// in "errordocs.js". Note: this only tests script execution. + +"use strict"; + +const ErrorDocs = require("devtools/server/actors/errordocs"); + +function makeURIData(script) { + return `data:text/html;charset=utf8,<script>${script}</script>`; +} + +const TestData = [ + { + jsmsg: "JSMSG_READ_ONLY", + script: "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;", + isException: true, + }, + { + jsmsg: "JSMSG_STMT_AFTER_RETURN", + script: "function a() { return; 1 + 1; };", + isException: false, + } +]; + +add_task(function* () { + yield loadTab("data:text/html;charset=utf8,errordoc tests"); + + let hud = yield openConsole(); + + for (let i = 0; i < TestData.length; i++) { + yield testScriptError(hud, TestData[i]); + } +}); + +function* testScriptError(hud, testData) { + if (testData.isException === true) { + expectUncaughtException(); + } + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, makeURIData(testData.script)); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + category: CATEGORY_JS + } + ] + }); + + // grab the most current error doc URL + let url = ErrorDocs.GetURL({ errorMessageName: testData.jsmsg }); + + let hrefs = {}; + for (let link of hud.jsterm.outputNode.querySelectorAll("a")) { + hrefs[link.href] = true; + } + + ok(url in hrefs, `Expected a link to ${url}.`); + + hud.jsterm.clearOutput(); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js new file mode 100644 index 000000000..43cb96bdc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js @@ -0,0 +1,39 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure non-toplevel security errors are displayed + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console subresource STS " + + "warning test"; +const TEST_DOC = "https://example.com/browser/devtools/client/webconsole/" + + "test/test_bug1092055_shouldwarn.html"; +const SAMPLE_MSG = "specified a header that could not be parsed successfully."; + +add_task(function* setup() { + yield SpecialPowers.pushPrefEnv({ + set: [["dom.ipc.processCount", 1]] + }); +}); + +add_task(function* () { + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + hud.jsterm.clearOutput(); + + let loaded = loadBrowser(browser); + BrowserTestUtils.loadURI(browser, TEST_DOC); + yield loaded; + + yield waitForSuccess({ + name: "Subresource STS warning displayed successfully", + validator: function () { + return hud.outputNode.textContent.indexOf(SAMPLE_MSG) > -1; + } + }); +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js b/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js new file mode 100644 index 000000000..b66d5afff --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js @@ -0,0 +1,73 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf8,Test that the web console " + + "displays requests that have been recorded in the " + + "netmonitor, even if the console hadn't opened yet."; + +const TEST_FILE = "test-network-request.html"; +const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/" + + "test/" + TEST_FILE; + +const NET_PREF = "devtools.webconsole.filter.networkinfo"; +Services.prefs.setBoolPref(NET_PREF, true); +registerCleanupFunction(() => { + Services.prefs.clearUserPref(NET_PREF); +}); + +add_task(function* () { + let { tab, browser } = yield loadTab(TEST_URI); + + let target = TargetFactory.forTab(tab); + let toolbox = yield gDevTools.showToolbox(target, "netmonitor"); + info("Network panel is open."); + + yield loadDocument(browser); + info("Document loaded."); + + // Test that the request appears in the network panel. + testNetmonitor(toolbox); + + // Test that the request appears in the console. + let hud = yield openConsole(); + info("Web console is open"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "network message", + text: TEST_FILE, + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG + } + ] + }); +}); + +function loadDocument(browser) { + let deferred = promise.defer(); + + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + deferred.resolve(); + }, true); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH); + + return deferred.promise; +} + +function testNetmonitor(toolbox) { + let monitor = toolbox.getCurrentPanel(); + let { RequestsMenu } = monitor.panelWin.NetMonitorView; + RequestsMenu.lazyUpdate = false; + is(RequestsMenu.itemCount, 1, "Network request appears in the network panel"); + + let item = RequestsMenu.getItemAtIndex(0); + is(item.attachment.method, "GET", "The attached method is correct."); + is(item.attachment.url, TEST_PATH, "The attached url is correct."); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_split.js b/devtools/client/webconsole/test/browser_webconsole_split.js new file mode 100644 index 000000000..0242d94b4 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_split.js @@ -0,0 +1,268 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting"; + +function test() { + waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest); +} + +function runTest() { + // Test is slow on Linux EC2 instances - Bug 962931 + requestLongerTimeout(2); + + let {Toolbox} = require("devtools/client/framework/toolbox"); + let toolbox; + + loadTab(TEST_URI).then(testConsoleLoadOnDifferentPanel); + + function testConsoleLoadOnDifferentPanel() { + info("About to check console loads even when non-webconsole panel is open"); + + openPanel("inspector").then(() => { + toolbox.on("webconsole-ready", () => { + ok(true, "Webconsole has been triggered as loaded while another tool " + + "is active"); + testKeyboardShortcuts(); + }); + + // Opens split console. + toolbox.toggleSplitConsole(); + }); + } + + function testKeyboardShortcuts() { + info("About to check that panel responds to ESCAPE keyboard shortcut"); + + toolbox.once("split-console", () => { + ok(true, "Split console has been triggered via ESCAPE keypress"); + checkAllTools(); + }); + + // Closes split console. + EventUtils.sendKey("ESCAPE", toolbox.win); + } + + function checkAllTools() { + info("About to check split console with each panel individually."); + + Task.spawn(function* () { + yield openAndCheckPanel("jsdebugger"); + yield openAndCheckPanel("inspector"); + yield openAndCheckPanel("styleeditor"); + yield openAndCheckPanel("performance"); + yield openAndCheckPanel("netmonitor"); + + yield checkWebconsolePanelOpened(); + testBottomHost(); + }); + } + + function getCurrentUIState() { + let win = toolbox.win; + let deck = toolbox.doc.querySelector("#toolbox-deck"); + let webconsolePanel = toolbox.webconsolePanel; + let splitter = toolbox.doc.querySelector("#toolbox-console-splitter"); + + let containerHeight = parseFloat(win.getComputedStyle(deck.parentNode) + .getPropertyValue("height")); + let deckHeight = parseFloat(win.getComputedStyle(deck) + .getPropertyValue("height")); + let webconsoleHeight = parseFloat(win.getComputedStyle(webconsolePanel) + .getPropertyValue("height")); + let splitterVisibility = !splitter.getAttribute("hidden"); + let openedConsolePanel = toolbox.currentToolId === "webconsole"; + let cmdButton = toolbox.doc.querySelector("#command-button-splitconsole"); + + return { + deckHeight: deckHeight, + containerHeight: containerHeight, + webconsoleHeight: webconsoleHeight, + splitterVisibility: splitterVisibility, + openedConsolePanel: openedConsolePanel, + buttonSelected: cmdButton.hasAttribute("checked") + }; + } + + function checkWebconsolePanelOpened() { + info("About to check special cases when webconsole panel is open."); + + let deferred = promise.defer(); + + // Start with console split, so we can test for transition to main panel. + toolbox.toggleSplitConsole(); + + let currentUIState = getCurrentUIState(); + + ok(currentUIState.splitterVisibility, + "Splitter is visible when console is split"); + ok(currentUIState.deckHeight > 0, + "Deck has a height > 0 when console is split"); + ok(currentUIState.webconsoleHeight > 0, + "Web console has a height > 0 when console is split"); + ok(!currentUIState.openedConsolePanel, + "The console panel is not the current tool"); + ok(currentUIState.buttonSelected, "The command button is selected"); + + openPanel("webconsole").then(() => { + currentUIState = getCurrentUIState(); + + ok(!currentUIState.splitterVisibility, + "Splitter is hidden when console is opened."); + is(currentUIState.deckHeight, 0, + "Deck has a height == 0 when console is opened."); + is(currentUIState.webconsoleHeight, currentUIState.containerHeight, + "Web console is full height."); + ok(currentUIState.openedConsolePanel, + "The console panel is the current tool"); + ok(currentUIState.buttonSelected, + "The command button is still selected."); + + // Make sure splitting console does nothing while webconsole is opened + toolbox.toggleSplitConsole(); + + currentUIState = getCurrentUIState(); + + ok(!currentUIState.splitterVisibility, + "Splitter is hidden when console is opened."); + is(currentUIState.deckHeight, 0, + "Deck has a height == 0 when console is opened."); + is(currentUIState.webconsoleHeight, currentUIState.containerHeight, + "Web console is full height."); + ok(currentUIState.openedConsolePanel, + "The console panel is the current tool"); + ok(currentUIState.buttonSelected, + "The command button is still selected."); + + // Make sure that split state is saved after opening another panel + openPanel("inspector").then(() => { + currentUIState = getCurrentUIState(); + ok(currentUIState.splitterVisibility, + "Splitter is visible when console is split"); + ok(currentUIState.deckHeight > 0, + "Deck has a height > 0 when console is split"); + ok(currentUIState.webconsoleHeight > 0, + "Web console has a height > 0 when console is split"); + ok(!currentUIState.openedConsolePanel, + "The console panel is not the current tool"); + ok(currentUIState.buttonSelected, + "The command button is still selected."); + + toolbox.toggleSplitConsole(); + deferred.resolve(); + }); + }); + return deferred.promise; + } + + function openPanel(toolId) { + let deferred = promise.defer(); + let target = TargetFactory.forTab(gBrowser.selectedTab); + gDevTools.showToolbox(target, toolId).then(function (box) { + toolbox = box; + deferred.resolve(); + }).then(null, console.error); + return deferred.promise; + } + + function openAndCheckPanel(toolId) { + let deferred = promise.defer(); + openPanel(toolId).then(() => { + info("Checking toolbox for " + toolId); + checkToolboxUI(toolbox.getCurrentPanel()); + deferred.resolve(); + }); + return deferred.promise; + } + + function checkToolboxUI() { + let currentUIState = getCurrentUIState(); + + ok(!currentUIState.splitterVisibility, "Splitter is hidden by default"); + is(currentUIState.deckHeight, currentUIState.containerHeight, + "Deck has a height > 0 by default"); + is(currentUIState.webconsoleHeight, 0, + "Web console is collapsed by default"); + ok(!currentUIState.openedConsolePanel, + "The console panel is not the current tool"); + ok(!currentUIState.buttonSelected, "The command button is not selected."); + + toolbox.toggleSplitConsole(); + + currentUIState = getCurrentUIState(); + + ok(currentUIState.splitterVisibility, + "Splitter is visible when console is split"); + ok(currentUIState.deckHeight > 0, + "Deck has a height > 0 when console is split"); + ok(currentUIState.webconsoleHeight > 0, + "Web console has a height > 0 when console is split"); + is(Math.round(currentUIState.deckHeight + currentUIState.webconsoleHeight), + currentUIState.containerHeight, + "Everything adds up to container height"); + ok(!currentUIState.openedConsolePanel, + "The console panel is not the current tool"); + ok(currentUIState.buttonSelected, "The command button is selected."); + + toolbox.toggleSplitConsole(); + + currentUIState = getCurrentUIState(); + + ok(!currentUIState.splitterVisibility, "Splitter is hidden after toggling"); + is(currentUIState.deckHeight, currentUIState.containerHeight, + "Deck has a height > 0 after toggling"); + is(currentUIState.webconsoleHeight, 0, + "Web console is collapsed after toggling"); + ok(!currentUIState.openedConsolePanel, + "The console panel is not the current tool"); + ok(!currentUIState.buttonSelected, "The command button is not selected."); + } + + function testBottomHost() { + checkHostType(Toolbox.HostType.BOTTOM); + + checkToolboxUI(); + + toolbox.switchHost(Toolbox.HostType.SIDE).then(testSidebarHost); + } + + function testSidebarHost() { + checkHostType(Toolbox.HostType.SIDE); + + checkToolboxUI(); + + toolbox.switchHost(Toolbox.HostType.WINDOW).then(testWindowHost); + } + + function testWindowHost() { + checkHostType(Toolbox.HostType.WINDOW); + + checkToolboxUI(); + + toolbox.switchHost(Toolbox.HostType.BOTTOM).then(testDestroy); + } + + function checkHostType(hostType) { + is(toolbox.hostType, hostType, "host type is " + hostType); + + let pref = Services.prefs.getCharPref("devtools.toolbox.host"); + is(pref, hostType, "host pref is " + hostType); + } + + function testDestroy() { + toolbox.destroy().then(function () { + let target = TargetFactory.forTab(gBrowser.selectedTab); + gDevTools.showToolbox(target).then(finish); + }); + } + + function finish() { + toolbox = null; + finishTest(); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js b/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js new file mode 100644 index 000000000..f71efb99e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js @@ -0,0 +1,158 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + "use strict"; + + function test() { + info("Test various cases where the escape key should hide the split console."); + + let toolbox; + let hud; + let jsterm; + let hudMessages; + let variablesView; + + Task.spawn(runner).then(finish); + + function* runner() { + let {tab} = yield loadTab("data:text/html;charset=utf-8,<p>Web Console " + + "test for splitting"); + let target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + yield testCreateSplitConsoleAfterEscape(); + + yield showAutoCompletePopoup(); + + yield testHideAutoCompletePopupAfterEscape(); + + yield executeJS(); + yield clickMessageAndShowVariablesView(); + jsterm.focus(); + + yield testHideVariablesViewAfterEscape(); + + yield clickMessageAndShowVariablesView(); + yield startPropertyEditor(); + + yield testCancelPropertyEditorAfterEscape(); + yield testHideVariablesViewAfterEscape(); + yield testHideSplitConsoleAfterEscape(); + } + + function testCreateSplitConsoleAfterEscape() { + let result = toolbox.once("webconsole-ready", () => { + hud = toolbox.getPanel("webconsole").hud; + jsterm = hud.jsterm; + ok(toolbox.splitConsole, "Split console is created."); + }); + + let contentWindow = toolbox.win; + contentWindow.focus(); + EventUtils.sendKey("ESCAPE", contentWindow); + + return result; + } + + function testHideSplitConsoleAfterEscape() { + let result = toolbox.once("split-console", () => { + ok(!toolbox.splitConsole, "Split console is hidden."); + }); + EventUtils.sendKey("ESCAPE", toolbox.win); + + return result; + } + + function testHideVariablesViewAfterEscape() { + let result = jsterm.once("sidebar-closed", () => { + ok(!hud.ui.jsterm.sidebar, + "Variables view is hidden."); + ok(toolbox.splitConsole, + "Split console is open after hiding the variables view."); + }); + EventUtils.sendKey("ESCAPE", toolbox.win); + + return result; + } + + function testHideAutoCompletePopupAfterEscape() { + let deferred = promise.defer(); + let popup = jsterm.autocompletePopup; + + popup.once("popup-closed", () => { + ok(!popup.isOpen, + "Auto complete popup is hidden."); + ok(toolbox.splitConsole, + "Split console is open after hiding the autocomplete popup."); + + deferred.resolve(); + }); + + EventUtils.sendKey("ESCAPE", toolbox.win); + + return deferred.promise; + } + + function testCancelPropertyEditorAfterEscape() { + EventUtils.sendKey("ESCAPE", variablesView.window); + ok(hud.ui.jsterm.sidebar, + "Variables view is open after canceling property editor."); + ok(toolbox.splitConsole, + "Split console is open after editing."); + } + + function* executeJS() { + jsterm.execute("var foo = { bar: \"baz\" }; foo;"); + hudMessages = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Object { bar: \"baz\" }", + category: CATEGORY_OUTPUT, + objects: true + }], + }); + } + + function clickMessageAndShowVariablesView() { + let result = jsterm.once("variablesview-fetched", (event, vview) => { + variablesView = vview; + }); + + let clickable = hudMessages[0].clickableElements[0]; + EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow); + + return result; + } + + function* startPropertyEditor() { + let results = yield findVariableViewProperties(variablesView, [ + {name: "bar", value: "baz"} + ], {webconsole: hud}); + results[0].matchedProp.focus(); + EventUtils.synthesizeKey("VK_RETURN", variablesView.window); + } + + function showAutoCompletePopoup() { + let onPopupShown = jsterm.autocompletePopup.once("popup-opened"); + + jsterm.focus(); + jsterm.setInputValue("document.location."); + EventUtils.sendKey("TAB", hud.iframeWindow); + + return onPopupShown; + } + + function finish() { + toolbox.destroy().then(() => { + toolbox = null; + hud = null; + jsterm = null; + hudMessages = null; + variablesView = null; + + finishTest(); + }); + } + } diff --git a/devtools/client/webconsole/test/browser_webconsole_split_focus.js b/devtools/client/webconsole/test/browser_webconsole_split_focus.js new file mode 100644 index 000000000..ff65229c9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_split_focus.js @@ -0,0 +1,66 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + "use strict"; + + function test() { + info("Test that the split console state is persisted"); + + let toolbox; + let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " + + "splitting</p>"; + + Task.spawn(runner).then(finish); + + function* runner() { + info("Opening a tab while there is no user setting on split console pref"); + let {tab} = yield loadTab(TEST_URI); + let target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + ok(!toolbox.splitConsole, "Split console is hidden by default"); + + info("Focusing the search box before opening the split console"); + let inspector = toolbox.getPanel("inspector"); + inspector.searchBox.focus(); + + let activeElement = getActiveElement(inspector.panelDoc); + is(activeElement, inspector.searchBox, "Search box is focused"); + + yield toolbox.openSplitConsole(); + + ok(toolbox.splitConsole, "Split console is now visible"); + + // Use the binding element since jsterm.inputNode is a XUL textarea element. + activeElement = getActiveElement(toolbox.doc); + activeElement = activeElement.ownerDocument.getBindingParent(activeElement); + let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode; + is(activeElement, inputNode, "Split console input is focused by default"); + + yield toolbox.closeSplitConsole(); + + info("Making sure that the search box is refocused after closing the " + + "split console"); + activeElement = getActiveElement(inspector.panelDoc); + is(activeElement, inspector.searchBox, "Search box is focused"); + + yield toolbox.destroy(); + } + + function getActiveElement(doc) { + let activeElement = doc.activeElement; + while (activeElement && activeElement.contentDocument) { + activeElement = activeElement.contentDocument.activeElement; + } + return activeElement; + } + + function finish() { + toolbox = TEST_URI = null; + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight"); + finishTest(); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_split_persist.js b/devtools/client/webconsole/test/browser_webconsole_split_persist.js new file mode 100644 index 000000000..e11bd4811 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_split_persist.js @@ -0,0 +1,119 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + + "use strict"; + + function test() { + info("Test that the split console state is persisted"); + + let toolbox; + let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " + + "splitting</p>"; + + Task.spawn(runner).then(finish); + + function* runner() { + info("Opening a tab while there is no user setting on split console pref"); + let {tab} = yield loadTab(TEST_URI); + let target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + ok(!toolbox.splitConsole, "Split console is hidden by default."); + ok(!isCommandButtonChecked(), "Split console button is unchecked by " + + "default."); + yield toggleSplitConsoleWithEscape(); + ok(toolbox.splitConsole, "Split console is now visible."); + ok(isCommandButtonChecked(), "Split console button is now checked."); + ok(getVisiblePrefValue(), "Visibility pref is true"); + + is(getHeightPrefValue(), toolbox.webconsolePanel.height, + "Panel height matches the pref"); + toolbox.webconsolePanel.height = 200; + + yield toolbox.destroy(); + + info("Opening a tab while there is a true user setting on split console " + + "pref"); + ({tab} = yield loadTab(TEST_URI)); + target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + ok(toolbox.splitConsole, "Split console is visible by default."); + ok(isCommandButtonChecked(), "Split console button is checked by default."); + is(getHeightPrefValue(), 200, "Height is set based on panel height after " + + "closing"); + + // Use the binding element since jsterm.inputNode is a XUL textarea element. + let activeElement = getActiveElement(toolbox.doc); + activeElement = activeElement.ownerDocument.getBindingParent(activeElement); + let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode; + is(activeElement, inputNode, "Split console input is focused by default"); + + toolbox.webconsolePanel.height = 1; + ok(toolbox.webconsolePanel.clientHeight > 1, + "The actual height of the console is bound with a min height"); + + toolbox.webconsolePanel.height = 10000; + ok(toolbox.webconsolePanel.clientHeight < 10000, + "The actual height of the console is bound with a max height"); + + yield toggleSplitConsoleWithEscape(); + ok(!toolbox.splitConsole, "Split console is now hidden."); + ok(!isCommandButtonChecked(), "Split console button is now unchecked."); + ok(!getVisiblePrefValue(), "Visibility pref is false"); + + yield toolbox.destroy(); + + is(getHeightPrefValue(), 10000, + "Height is set based on panel height after closing"); + + info("Opening a tab while there is a false user setting on split " + + "console pref"); + ({tab} = yield loadTab(TEST_URI)); + target = TargetFactory.forTab(tab); + toolbox = yield gDevTools.showToolbox(target, "inspector"); + + ok(!toolbox.splitConsole, "Split console is hidden by default."); + ok(!getVisiblePrefValue(), "Visibility pref is false"); + + yield toolbox.destroy(); + } + + function getActiveElement(doc) { + let activeElement = doc.activeElement; + while (activeElement && activeElement.contentDocument) { + activeElement = activeElement.contentDocument.activeElement; + } + return activeElement; + } + + function getVisiblePrefValue() { + return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled"); + } + + function getHeightPrefValue() { + return Services.prefs.getIntPref("devtools.toolbox.splitconsoleHeight"); + } + + function isCommandButtonChecked() { + return toolbox.doc.querySelector("#command-button-splitconsole") + .hasAttribute("checked"); + } + + function toggleSplitConsoleWithEscape() { + let onceSplitConsole = toolbox.once("split-console"); + let contentWindow = toolbox.win; + contentWindow.focus(); + EventUtils.sendKey("ESCAPE", contentWindow); + return onceSplitConsole; + } + + function finish() { + toolbox = TEST_URI = null; + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled"); + Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight"); + finishTest(); + } +} diff --git a/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js b/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js new file mode 100644 index 000000000..a10acf9b2 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js @@ -0,0 +1,38 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that the webconsole works if the network monitor is first opened, then +// the user switches to the webconsole. See bug 970914. + +"use strict"; + +function test() { + Task.spawn(runner).then(finishTest); + + function* runner() { + const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello"); + + const hud = yield openConsole(tab); + + hud.jsterm.execute("console.log('foobar bug970914')"); + + yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log", + text: "foobar bug970914", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let text = hud.outputNode.textContent; + isnot(text.indexOf("foobar bug970914"), -1, + "console.log message confirmed"); + ok(!/logging API|disabled by a script/i.test(text), + "no warning about disabled console API"); + } +} + diff --git a/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js b/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js new file mode 100644 index 000000000..c8f2200f9 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js @@ -0,0 +1,83 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that "use strict" JS errors generate errors, not warnings. + +"use strict"; + +add_task(function* () { + // On e10s, the exception is triggered in child process + // and is ignored by test harness + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + yield loadTab("data:text/html;charset=utf8,<script>'use strict';var arguments;</script>"); + + let hud = yield openConsole(); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "SyntaxError: 'arguments' can't be defined or assigned to in strict mode code", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + ], + }); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "data:text/html;charset=" + + "utf8,<script>'use strict';function f(a, a) {};</script>"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "SyntaxError: duplicate formal argument a", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + ], + }); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "data:text/html;charset=" + + "utf8,<script>'use strict';var o = {get p() {}};o.p = 1;</script>"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "TypeError: setting a property that has only a getter", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + ], + }); + + if (!Services.appinfo.browserTabsRemoteAutostart) { + expectUncaughtException(); + } + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, + "data:text/html;charset=utf8,<script>'use strict';v = 1;</script>"); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + text: "ReferenceError: assignment to undeclared variable v", + category: CATEGORY_JS, + severity: SEVERITY_ERROR, + }, + ], + }); + + hud = null; +}); diff --git a/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js b/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js new file mode 100644 index 000000000..eafeee18e --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js @@ -0,0 +1,54 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Load a page with tracking elements that get blocked and make sure that a +// 'learn more' link shows up in the webconsole. + +"use strict"; + +const TEST_URI = "http://tracking.example.org/browser/devtools/client/" + + "webconsole/test/test-trackingprotection-securityerrors.html"; +const LEARN_MORE_URI = "https://developer.mozilla.org/Firefox/Privacy/" + + "Tracking_Protection" + DOCS_GA_PARAMS; +const PREF = "privacy.trackingprotection.enabled"; + +const {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {}); + +registerCleanupFunction(function () { + Services.prefs.clearUserPref(PREF); + UrlClassifierTestUtils.cleanupTestTrackers(); +}); + +add_task(function* testMessagesAppear() { + yield UrlClassifierTestUtils.addTestTrackers(); + Services.prefs.setBoolPref(PREF, true); + + let { browser } = yield loadTab(TEST_URI); + + let hud = yield openConsole(); + + let results = yield waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Was blocked because tracking protection is enabled", + text: "The resource at \u201chttp://tracking.example.com/\u201d was " + + "blocked because tracking protection is enabled", + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING, + objects: true, + }, + ], + }); + + yield testClickOpenNewTab(hud, results[0]); +}); + +function testClickOpenNewTab(hud, match) { + let warningNode = match.clickableElements[0]; + ok(warningNode, "link element"); + ok(warningNode.classList.contains("learn-more-link"), "link class name"); + return simulateMessageLinkClick(warningNode, LEARN_MORE_URI); +} diff --git a/devtools/client/webconsole/test/browser_webconsole_view_source.js b/devtools/client/webconsole/test/browser_webconsole_view_source.js new file mode 100644 index 000000000..a81b58acc --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js @@ -0,0 +1,52 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that source URLs in the Web Console can be clicked to display the +// standard View Source window. As JS exceptions and console.log() messages always +// have their locations opened in Debugger, we need to test a security message in +// order to have it opened in the standard View Source window. + +"use strict"; + +const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" + + "test/test-mixedcontent-securityerrors.html"; + +add_task(function* () { + yield actuallyTest(); +}); + +add_task(function* () { + Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false); + yield actuallyTest(); + Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend"); +}); + +var actuallyTest = Task.async(function*() { + yield loadTab(TEST_URI); + let hud = yield openConsole(null); + info("console opened"); + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + text: "Blocked loading mixed active content", + category: CATEGORY_SECURITY, + severity: SEVERITY_ERROR, + }], + }); + + let msg = [...result.matched][0]; + ok(msg, "error message"); + let locationNode = msg.querySelector(".message-location .frame-link-filename"); + ok(locationNode, "location node"); + + let onTabOpen = waitForTab(); + + EventUtils.sendMouseEvent({ type: "click" }, locationNode); + + let tab = yield onTabOpen; + ok(true, "the view source tab was opened in response to clicking the location node"); + gBrowser.removeTab(tab); +}); diff --git a/devtools/client/webconsole/test/head.js b/devtools/client/webconsole/test/head.js new file mode 100644 index 000000000..519cb78b0 --- /dev/null +++ b/devtools/client/webconsole/test/head.js @@ -0,0 +1,1844 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* import-globals-from ../../framework/test/shared-head.js */ +"use strict"; + +// shared-head.js handles imports, constants, and utility functions +Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this); + +var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils"); +var {Messages} = require("devtools/client/webconsole/console-output"); +const asyncStorage = require("devtools/shared/async-storage"); +const HUDService = require("devtools/client/webconsole/hudservice"); + +// Services.prefs.setBoolPref("devtools.debugger.log", true); + +var gPendingOutputTest = 0; + +// The various categories of messages. +const CATEGORY_NETWORK = 0; +const CATEGORY_CSS = 1; +const CATEGORY_JS = 2; +const CATEGORY_WEBDEV = 3; +const CATEGORY_INPUT = 4; +const CATEGORY_OUTPUT = 5; +const CATEGORY_SECURITY = 6; +const CATEGORY_SERVER = 7; + +// The possible message severities. +const SEVERITY_ERROR = 0; +const SEVERITY_WARNING = 1; +const SEVERITY_INFO = 2; +const SEVERITY_LOG = 3; + +// The indent of a console group in pixels. +const GROUP_INDENT = 12; + +const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties"; +var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI); + +const DOCS_GA_PARAMS = "?utm_source=mozilla" + + "&utm_medium=firefox-console-errors" + + "&utm_campaign=default"; + +flags.testing = true; + +function loadTab(url) { + let deferred = promise.defer(); + + let tab = gBrowser.selectedTab = gBrowser.addTab(url); + let browser = gBrowser.getBrowserForTab(tab); + + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + deferred.resolve({tab: tab, browser: browser}); + }, true); + + return deferred.promise; +} + +function loadBrowser(browser) { + return BrowserTestUtils.browserLoaded(browser); +} + +function closeTab(tab) { + let deferred = promise.defer(); + + let container = gBrowser.tabContainer; + + container.addEventListener("TabClose", function onTabClose() { + container.removeEventListener("TabClose", onTabClose, true); + deferred.resolve(null); + }, true); + + gBrowser.removeTab(tab); + + return deferred.promise; +} + +/** + * Load the page and return the associated HUD. + * + * @param string uri + * The URI of the page to load. + * @param string consoleType [optional] + * The console type, either "browserConsole" or "webConsole". Defaults to + * "webConsole". + * @return object + * The HUD associated with the console + */ +function* loadPageAndGetHud(uri, consoleType) { + let { browser } = yield loadTab("data:text/html;charset=utf-8,Loading tab for tests"); + + let hud; + if (consoleType === "browserConsole") { + hud = yield HUDService.openBrowserConsoleOrFocus(); + } else { + hud = yield openConsole(); + } + + ok(hud, "Console was opened"); + + let loaded = loadBrowser(browser); + yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri); + yield loaded; + + yield waitForMessages({ + webconsole: hud, + messages: [{ + text: uri, + category: CATEGORY_NETWORK, + severity: SEVERITY_LOG, + }], + }); + + return hud; +} + +function afterAllTabsLoaded(callback, win) { + win = win || window; + + let stillToLoad = 0; + + function onLoad() { + this.removeEventListener("load", onLoad, true); + stillToLoad--; + if (!stillToLoad) { + callback(); + } + } + + for (let a = 0; a < win.gBrowser.tabs.length; a++) { + let browser = win.gBrowser.tabs[a].linkedBrowser; + if (browser.webProgress.isLoadingDocument) { + stillToLoad++; + browser.addEventListener("load", onLoad, true); + } + } + + if (!stillToLoad) { + callback(); + } +} + +/** + * Check if a log entry exists in the HUD output node. + * + * @param {Element} outputNode + * the HUD output node. + * @param {string} matchString + * the string you want to check if it exists in the output node. + * @param {string} msg + * the message describing the test + * @param {boolean} [onlyVisible=false] + * find only messages that are visible, not hidden by the filter. + * @param {boolean} [failIfFound=false] + * fail the test if the string is found in the output node. + * @param {string} cssClass [optional] + * find only messages with the given CSS class. + */ +function testLogEntry(outputNode, matchString, msg, onlyVisible, + failIfFound, cssClass) { + let selector = ".message"; + // Skip entries that are hidden by the filter. + if (onlyVisible) { + selector += ":not(.filtered-by-type):not(.filtered-by-string)"; + } + if (cssClass) { + selector += "." + aClass; + } + + let msgs = outputNode.querySelectorAll(selector); + let found = false; + for (let i = 0, n = msgs.length; i < n; i++) { + let message = msgs[i].textContent.indexOf(matchString); + if (message > -1) { + found = true; + break; + } + } + + is(found, !failIfFound, msg); +} + +/** + * A convenience method to call testLogEntry(). + * + * @param str string + * The string to find. + */ +function findLogEntry(str) { + testLogEntry(outputNode, str, "found " + str); +} + +/** + * Open the Web Console for the given tab. + * + * @param nsIDOMElement [tab] + * Optional tab element for which you want open the Web Console. The + * default tab is taken from the global variable |tab|. + * @param function [callback] + * Optional function to invoke after the Web Console completes + * initialization (web-console-created). + * @return object + * A promise that is resolved once the web console is open. + */ +var openConsole = function (tab) { + let webconsoleOpened = promise.defer(); + let target = TargetFactory.forTab(tab || gBrowser.selectedTab); + gDevTools.showToolbox(target, "webconsole").then(toolbox => { + let hud = toolbox.getCurrentPanel().hud; + hud.jsterm._lazyVariablesView = false; + webconsoleOpened.resolve(hud); + }); + return webconsoleOpened.promise; +}; + +/** + * Close the Web Console for the given tab. + * + * @param nsIDOMElement [tab] + * Optional tab element for which you want close the Web Console. The + * default tab is taken from the global variable |tab|. + * @param function [callback] + * Optional function to invoke after the Web Console completes + * closing (web-console-destroyed). + * @return object + * A promise that is resolved once the web console is closed. + */ +var closeConsole = Task.async(function* (tab) { + let target = TargetFactory.forTab(tab || gBrowser.selectedTab); + let toolbox = gDevTools.getToolbox(target); + if (toolbox) { + yield toolbox.destroy(); + } +}); + +/** + * Listen for a new tab to open and return a promise that resolves when one + * does and completes the load event. + * @return a promise that resolves to the tab object + */ +var waitForTab = Task.async(function* () { + info("Waiting for a tab to open"); + yield once(gBrowser.tabContainer, "TabOpen"); + let tab = gBrowser.selectedTab; + let browser = tab.linkedBrowser; + yield once(browser, "load", true); + info("The tab load completed"); + return tab; +}); + +/** + * Dump the output of all open Web Consoles - used only for debugging purposes. + */ +function dumpConsoles() { + if (gPendingOutputTest) { + console.log("dumpConsoles start"); + for (let [, hud] of HUDService.consoles) { + if (!hud.outputNode) { + console.debug("no output content for", hud.hudId); + continue; + } + + console.debug("output content for", hud.hudId); + for (let elem of hud.outputNode.childNodes) { + dumpMessageElement(elem); + } + } + console.log("dumpConsoles end"); + + gPendingOutputTest = 0; + } +} + +/** + * Dump to output debug information for the given webconsole message. + * + * @param nsIDOMNode message + * The message element you want to display. + */ +function dumpMessageElement(message) { + let text = message.textContent; + let repeats = message.querySelector(".message-repeats"); + if (repeats) { + repeats = repeats.getAttribute("value"); + } + console.debug("id", message.getAttribute("id"), + "date", message.timestamp, + "class", message.className, + "category", message.category, + "severity", message.severity, + "repeats", repeats, + "clipboardText", message.clipboardText, + "text", text); +} + +var finishTest = Task.async(function* () { + dumpConsoles(); + + let browserConsole = HUDService.getBrowserConsole(); + if (browserConsole) { + if (browserConsole.jsterm) { + browserConsole.jsterm.clearOutput(true); + } + yield HUDService.toggleBrowserConsole(); + } + + let target = TargetFactory.forTab(gBrowser.selectedTab); + yield gDevTools.closeToolbox(target); + + finish(); +}); + +// Always use the 'old' frontend for tests that rely on it +Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false); +registerCleanupFunction(function* () { + Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled"); +}); + +registerCleanupFunction(function* () { + flags.testing = false; + + // Remove stored console commands in between tests + yield asyncStorage.removeItem("webConsoleHistory"); + + dumpConsoles(); + + let browserConsole = HUDService.getBrowserConsole(); + if (browserConsole) { + if (browserConsole.jsterm) { + browserConsole.jsterm.clearOutput(true); + } + yield HUDService.toggleBrowserConsole(); + } + + let target = TargetFactory.forTab(gBrowser.selectedTab); + yield gDevTools.closeToolbox(target); + + while (gBrowser.tabs.length > 1) { + gBrowser.removeCurrentTab(); + } +}); + +waitForExplicitFinish(); + +/** + * Polls a given function waiting for it to become true. + * + * @param object options + * Options object with the following properties: + * - validator + * A validator function that returns a boolean. This is called every few + * milliseconds to check if the result is true. When it is true, the + * promise is resolved and polling stops. If validator never returns + * true, then polling timeouts after several tries and the promise is + * rejected. + * - name + * Name of test. This is used to generate the success and failure + * messages. + * - timeout + * Timeout for validator function, in milliseconds. Default is 5000. + * @return object + * A Promise object that is resolved based on the validator function. + */ +function waitForSuccess(options) { + let deferred = promise.defer(); + let start = Date.now(); + let timeout = options.timeout || 5000; + let {validator} = options; + + function wait() { + if ((Date.now() - start) > timeout) { + // Log the failure. + ok(false, "Timed out while waiting for: " + options.name); + deferred.reject(null); + return; + } + + if (validator(options)) { + ok(true, options.name); + deferred.resolve(null); + } else { + setTimeout(wait, 100); + } + } + + setTimeout(wait, 100); + + return deferred.promise; +} + +var openInspector = Task.async(function* (tab = gBrowser.selectedTab) { + let target = TargetFactory.forTab(tab); + let toolbox = yield gDevTools.showToolbox(target, "inspector"); + return toolbox.getCurrentPanel(); +}); + +/** + * Find variables or properties in a VariablesView instance. + * + * @param object view + * The VariablesView instance. + * @param array rules + * The array of rules you want to match. Each rule is an object with: + * - name (string|regexp): property name to match. + * - value (string|regexp): property value to match. + * - isIterator (boolean): check if the property is an iterator. + * - isGetter (boolean): check if the property is a getter. + * - isGenerator (boolean): check if the property is a generator. + * - dontMatch (boolean): make sure the rule doesn't match any property. + * @param object options + * Options for matching: + * - webconsole: the WebConsole instance we work with. + * @return object + * A promise object that is resolved when all the rules complete + * matching. The resolved callback is given an array of all the rules + * you wanted to check. Each rule has a new property: |matchedProp| + * which holds a reference to the Property object instance from the + * VariablesView. If the rule did not match, then |matchedProp| is + * undefined. + */ +function findVariableViewProperties(view, rules, options) { + // Initialize the search. + function init() { + // Separate out the rules that require expanding properties throughout the + // view. + let expandRules = []; + let filterRules = rules.filter((rule) => { + if (typeof rule.name == "string" && rule.name.indexOf(".") > -1) { + expandRules.push(rule); + return false; + } + return true; + }); + + // Search through the view those rules that do not require any properties to + // be expanded. Build the array of matchers, outstanding promises to be + // resolved. + let outstanding = []; + finder(filterRules, view, outstanding); + + // Process the rules that need to expand properties. + let lastStep = processExpandRules.bind(null, expandRules); + + // Return the results - a promise resolved to hold the updated rules array. + let returnResults = onAllRulesMatched.bind(null, rules); + + return promise.all(outstanding).then(lastStep).then(returnResults); + } + + function onMatch(prop, rule, matched) { + if (matched && !rule.matchedProp) { + rule.matchedProp = prop; + } + } + + function finder(rules, vars, promises) { + for (let [, prop] of vars) { + for (let rule of rules) { + let matcher = matchVariablesViewProperty(prop, rule, options); + promises.push(matcher.then(onMatch.bind(null, prop, rule))); + } + } + } + + function processExpandRules(rules) { + let rule = rules.shift(); + if (!rule) { + return promise.resolve(null); + } + + let deferred = promise.defer(); + let expandOptions = { + rootVariable: view, + expandTo: rule.name, + webconsole: options.webconsole, + }; + + variablesViewExpandTo(expandOptions).then(function onSuccess(prop) { + let name = rule.name; + let lastName = name.split(".").pop(); + rule.name = lastName; + + let matched = matchVariablesViewProperty(prop, rule, options); + return matched.then(onMatch.bind(null, prop, rule)).then(function () { + rule.name = name; + }); + }, function onFailure() { + return promise.resolve(null); + }).then(processExpandRules.bind(null, rules)).then(function () { + deferred.resolve(null); + }); + + return deferred.promise; + } + + function onAllRulesMatched(rules) { + for (let rule of rules) { + let matched = rule.matchedProp; + if (matched && !rule.dontMatch) { + ok(true, "rule " + rule.name + " matched for property " + matched.name); + } else if (matched && rule.dontMatch) { + ok(false, "rule " + rule.name + " should not match property " + + matched.name); + } else { + ok(rule.dontMatch, "rule " + rule.name + " did not match any property"); + } + } + return rules; + } + + return init(); +} + +/** + * Check if a given Property object from the variables view matches the given + * rule. + * + * @param object prop + * The variable's view Property instance. + * @param object rule + * Rules for matching the property. See findVariableViewProperties() for + * details. + * @param object options + * Options for matching. See findVariableViewProperties(). + * @return object + * A promise that is resolved when all the checks complete. Resolution + * result is a boolean that tells your promise callback the match + * result: true or false. + */ +function matchVariablesViewProperty(prop, rule, options) { + function resolve(result) { + return promise.resolve(result); + } + + if (rule.name) { + let match = rule.name instanceof RegExp ? + rule.name.test(prop.name) : + prop.name == rule.name; + if (!match) { + return resolve(false); + } + } + + if (rule.value) { + let displayValue = prop.displayValue; + if (prop.displayValueClassName == "token-string") { + displayValue = displayValue.substring(1, displayValue.length - 1); + } + + let match = rule.value instanceof RegExp ? + rule.value.test(displayValue) : + displayValue == rule.value; + if (!match) { + info("rule " + rule.name + " did not match value, expected '" + + rule.value + "', found '" + displayValue + "'"); + return resolve(false); + } + } + + if ("isGetter" in rule) { + let isGetter = !!(prop.getter && prop.get("get")); + if (rule.isGetter != isGetter) { + info("rule " + rule.name + " getter test failed"); + return resolve(false); + } + } + + if ("isGenerator" in rule) { + let isGenerator = prop.displayValue == "Generator"; + if (rule.isGenerator != isGenerator) { + info("rule " + rule.name + " generator test failed"); + return resolve(false); + } + } + + let outstanding = []; + + if ("isIterator" in rule) { + let isIterator = isVariableViewPropertyIterator(prop, options.webconsole); + outstanding.push(isIterator.then((result) => { + if (result != rule.isIterator) { + info("rule " + rule.name + " iterator test failed"); + } + return result == rule.isIterator; + })); + } + + outstanding.push(promise.resolve(true)); + + return promise.all(outstanding).then(function _onMatchDone(results) { + let ruleMatched = results.indexOf(false) == -1; + return resolve(ruleMatched); + }); +} + +/** + * Check if the given variables view property is an iterator. + * + * @param object prop + * The Property instance you want to check. + * @param object webConsole + * The WebConsole instance to work with. + * @return object + * A promise that is resolved when the check completes. The resolved + * callback is given a boolean: true if the property is an iterator, or + * false otherwise. + */ +function isVariableViewPropertyIterator(prop, webConsole) { + if (prop.displayValue == "Iterator") { + return promise.resolve(true); + } + + let deferred = promise.defer(); + + variablesViewExpandTo({ + rootVariable: prop, + expandTo: "__proto__.__iterator__", + webconsole: webConsole, + }).then(function onSuccess() { + deferred.resolve(true); + }, function onFailure() { + deferred.resolve(false); + }); + + return deferred.promise; +} + +/** + * Recursively expand the variables view up to a given property. + * + * @param options + * Options for view expansion: + * - rootVariable: start from the given scope/variable/property. + * - expandTo: string made up of property names you want to expand. + * For example: "body.firstChild.nextSibling" given |rootVariable: + * document|. + * - webconsole: a WebConsole instance. If this is not provided all + * property expand() calls will be considered sync. Things may fail! + * @return object + * A promise that is resolved only when the last property in |expandTo| + * is found, and rejected otherwise. Resolution reason is always the + * last property - |nextSibling| in the example above. Rejection is + * always the last property that was found. + */ +function variablesViewExpandTo(options) { + let root = options.rootVariable; + let expandTo = options.expandTo.split("."); + let jsterm = (options.webconsole || {}).jsterm; + let lastDeferred = promise.defer(); + + function fetch(prop) { + if (!prop.onexpand) { + ok(false, "property " + prop.name + " cannot be expanded: !onexpand"); + return promise.reject(prop); + } + + let deferred = promise.defer(); + + if (prop._fetched || !jsterm) { + executeSoon(function () { + deferred.resolve(prop); + }); + } else { + jsterm.once("variablesview-fetched", function _onFetchProp() { + executeSoon(() => deferred.resolve(prop)); + }); + } + + prop.expand(); + + return deferred.promise; + } + + function getNext(prop) { + let name = expandTo.shift(); + let newProp = prop.get(name); + + if (expandTo.length > 0) { + ok(newProp, "found property " + name); + if (newProp) { + fetch(newProp).then(getNext, fetchError); + } else { + lastDeferred.reject(prop); + } + } else if (newProp) { + lastDeferred.resolve(newProp); + } else { + lastDeferred.reject(prop); + } + } + + function fetchError(prop) { + lastDeferred.reject(prop); + } + + if (!root._fetched) { + fetch(root).then(getNext, fetchError); + } else { + getNext(root); + } + + return lastDeferred.promise; +} + +/** + * Update the content of a property in the variables view. + * + * @param object options + * Options for the property update: + * - property: the property you want to change. + * - field: string that tells what you want to change: + * - use "name" to change the property name, + * - or "value" to change the property value. + * - string: the new string to write into the field. + * - webconsole: reference to the Web Console instance we work with. + * @return object + * A Promise object that is resolved once the property is updated. + */ +var updateVariablesViewProperty = Task.async(function* (options) { + let view = options.property._variablesView; + view.window.focus(); + options.property.focus(); + + switch (options.field) { + case "name": + EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true }, view.window); + break; + case "value": + EventUtils.synthesizeKey("VK_RETURN", {}, view.window); + break; + default: + throw new Error("options.field is incorrect"); + } + + let deferred = promise.defer(); + + executeSoon(() => { + EventUtils.synthesizeKey("A", { accelKey: true }, view.window); + + for (let c of options.string) { + EventUtils.synthesizeKey(c, {}, view.window); + } + + if (options.webconsole) { + options.webconsole.jsterm.once("variablesview-fetched") + .then((varView) => deferred.resolve(varView)); + } + + EventUtils.synthesizeKey("VK_RETURN", {}, view.window); + + if (!options.webconsole) { + executeSoon(() => { + deferred.resolve(null); + }); + } + }); + + return deferred.promise; +}); + +/** + * Open the JavaScript debugger. + * + * @param object options + * Options for opening the debugger: + * - tab: the tab you want to open the debugger for. + * @return object + * A promise that is resolved once the debugger opens, or rejected if + * the open fails. The resolution callback is given one argument, an + * object that holds the following properties: + * - target: the Target object for the Tab. + * - toolbox: the Toolbox instance. + * - panel: the jsdebugger panel instance. + * - panelWin: the window object of the panel iframe. + */ +function openDebugger(options = {}) { + if (!options.tab) { + options.tab = gBrowser.selectedTab; + } + + let deferred = promise.defer(); + + let target = TargetFactory.forTab(options.tab); + let toolbox = gDevTools.getToolbox(target); + let dbgPanelAlreadyOpen = toolbox && toolbox.getPanel("jsdebugger"); + + gDevTools.showToolbox(target, "jsdebugger").then(function onSuccess(tool) { + let panel = tool.getCurrentPanel(); + let panelWin = panel.panelWin; + + panel._view.Variables.lazyEmpty = false; + + let resolveObject = { + target: target, + toolbox: tool, + panel: panel, + panelWin: panelWin, + }; + + if (dbgPanelAlreadyOpen) { + deferred.resolve(resolveObject); + } else { + panelWin.DebuggerController.waitForSourcesLoaded().then(() => { + deferred.resolve(resolveObject); + }); + } + }, function onFailure(reason) { + console.debug("failed to open the toolbox for 'jsdebugger'", reason); + deferred.reject(reason); + }); + + return deferred.promise; +} + +/** + * Returns true if the caret in the debugger editor is placed at the specified + * position. + * @param panel The debugger panel. + * @param {number} line The line number. + * @param {number} [col] The column number. + * @returns {boolean} + */ +function isDebuggerCaretPos(panel, line, col = 1) { + let editor = panel.panelWin.DebuggerView.editor; + let cursor = editor.getCursor(); + + // Source editor starts counting line and column numbers from 0. + info("Current editor caret position: " + (cursor.line + 1) + ", " + + (cursor.ch + 1)); + return cursor.line == (line - 1) && cursor.ch == (col - 1); +} + +/** + * Wait for messages in the Web Console output. + * + * @param object options + * Options for what you want to wait for: + * - webconsole: the webconsole instance you work with. + * - matchCondition: "any" or "all". Default: "all". The promise + * returned by this function resolves when all of the messages are + * matched, if the |matchCondition| is "all". If you set the condition to + * "any" then the promise is resolved by any message rule that matches, + * irrespective of order - waiting for messages stops whenever any rule + * matches. + * - messages: an array of objects that tells which messages to wait for. + * Properties: + * - text: string or RegExp to match the textContent of each new + * message. + * - noText: string or RegExp that must not match in the message + * textContent. + * - repeats: the number of message repeats, as displayed by the Web + * Console. + * - category: match message category. See CATEGORY_* constants at + * the top of this file. + * - severity: match message severity. See SEVERITY_* constants at + * the top of this file. + * - count: how many unique web console messages should be matched by + * this rule. + * - consoleTrace: boolean, set to |true| to match a console.trace() + * message. Optionally this can be an object of the form + * { file, fn, line } that can match the specified file, function + * and/or line number in the trace message. + * - consoleTime: string that matches a console.time() timer name. + * Provide this if you want to match a console.time() message. + * - consoleTimeEnd: same as above, but for console.timeEnd(). + * - consoleDir: boolean, set to |true| to match a console.dir() + * message. + * - consoleGroup: boolean, set to |true| to match a console.group() + * message. + * - consoleTable: boolean, set to |true| to match a console.table() + * message. + * - longString: boolean, set to |true} to match long strings in the + * message. + * - collapsible: boolean, set to |true| to match messages that can + * be collapsed/expanded. + * - type: match messages that are instances of the given object. For + * example, you can point to Messages.NavigationMarker to match any + * such message. + * - objects: boolean, set to |true| if you expect inspectable + * objects in the message. + * - source: object of the shape { url, line }. This is used to + * match the source URL and line number of the error message or + * console API call. + * - prefix: prefix text to check for in the prefix element. + * - stacktrace: array of objects of the form { file, fn, line } that + * can match frames in the stacktrace associated with the message. + * - groupDepth: number used to check the depth of the message in + * a group. + * - url: URL to match for network requests. + * @return object + * A promise object is returned once the messages you want are found. + * The promise is resolved with the array of rule objects you give in + * the |messages| property. Each objects is the same as provided, with + * additional properties: + * - matched: a Set of web console messages that matched the rule. + * - clickableElements: a list of inspectable objects. This is available + * if any of the following properties are present in the rule: + * |consoleTrace| or |objects|. + * - longStrings: a list of long string ellipsis elements you can click + * in the message element, to expand a long string. This is available + * only if |longString| is present in the matching rule. + */ +function waitForMessages(options) { + info("Waiting for messages..."); + + gPendingOutputTest++; + let webconsole = options.webconsole; + let rules = WebConsoleUtils.cloneObject(options.messages, true); + let rulesMatched = 0; + let listenerAdded = false; + let deferred = promise.defer(); + options.matchCondition = options.matchCondition || "all"; + + function checkText(rule, text) { + let result = false; + if (Array.isArray(rule)) { + result = rule.every((s) => checkText(s, text)); + } else if (typeof rule == "string") { + result = text.indexOf(rule) > -1; + } else if (rule instanceof RegExp) { + result = rule.test(text); + } else { + result = rule == text; + } + return result; + } + + function checkConsoleTable(rule, element) { + let elemText = element.textContent; + + if (!checkText("console.table():", elemText)) { + return false; + } + + rule.category = CATEGORY_WEBDEV; + rule.severity = SEVERITY_LOG; + rule.type = Messages.ConsoleTable; + + return true; + } + + function checkConsoleTrace(rule, element) { + let elemText = element.textContent; + let trace = rule.consoleTrace; + + if (!checkText("console.trace():", elemText)) { + return false; + } + + rule.category = CATEGORY_WEBDEV; + rule.severity = SEVERITY_LOG; + rule.type = Messages.ConsoleTrace; + + if (!rule.stacktrace && typeof trace == "object" && trace !== true) { + if (Array.isArray(trace)) { + rule.stacktrace = trace; + } else { + rule.stacktrace = [trace]; + } + } + + return true; + } + + function checkConsoleTime(rule, element) { + let elemText = element.textContent; + let time = rule.consoleTime; + + if (!checkText(time + ": timer started", elemText)) { + return false; + } + + rule.category = CATEGORY_WEBDEV; + rule.severity = SEVERITY_LOG; + + return true; + } + + function checkConsoleTimeEnd(rule, element) { + let elemText = element.textContent; + let time = rule.consoleTimeEnd; + let regex = new RegExp(time + ": -?\\d+([,.]\\d+)?ms"); + + if (!checkText(regex, elemText)) { + return false; + } + + rule.category = CATEGORY_WEBDEV; + rule.severity = SEVERITY_LOG; + + return true; + } + + function checkConsoleDir(rule, element) { + if (!element.classList.contains("inlined-variables-view")) { + return false; + } + + let elemText = element.textContent; + if (!checkText(rule.consoleDir, elemText)) { + return false; + } + + let iframe = element.querySelector("iframe"); + if (!iframe) { + ok(false, "console.dir message has no iframe"); + return false; + } + + return true; + } + + function checkConsoleGroup(rule) { + if (!isNaN(parseInt(rule.consoleGroup, 10))) { + rule.groupDepth = rule.consoleGroup; + } + rule.category = CATEGORY_WEBDEV; + rule.severity = SEVERITY_LOG; + + return true; + } + + function checkSource(rule, element) { + let location = getRenderedSource(element); + if (!location) { + return false; + } + + if (!checkText(rule.source.url, location.url)) { + return false; + } + + if ("line" in rule.source && location.line != rule.source.line) { + return false; + } + + return true; + } + + function checkCollapsible(rule, element) { + let msg = element._messageObject; + if (!msg || !!msg.collapsible != rule.collapsible) { + return false; + } + + return true; + } + + function checkStacktrace(rule, element) { + let stack = rule.stacktrace; + let frames = element.querySelectorAll(".stacktrace > .stack-trace > .frame-link"); + if (!frames.length) { + return false; + } + + for (let i = 0; i < stack.length; i++) { + let frame = frames[i]; + let expected = stack[i]; + if (!frame) { + ok(false, "expected frame #" + i + " but didnt find it"); + return false; + } + + if (expected.file) { + let url = frame.getAttribute("data-url"); + if (!checkText(expected.file, url)) { + ok(false, "frame #" + i + " does not match file name: " + + expected.file + " != " + url); + displayErrorContext(rule, element); + return false; + } + } + + if (expected.fn) { + let fn = frame.querySelector(".frame-link-function-display-name").textContent; + if (!checkText(expected.fn, fn)) { + ok(false, "frame #" + i + " does not match the function name: " + + expected.fn + " != " + fn); + displayErrorContext(rule, element); + return false; + } + } + + if (expected.line) { + let line = frame.getAttribute("data-line"); + if (!checkText(expected.line, line)) { + ok(false, "frame #" + i + " does not match the line number: " + + expected.line + " != " + line); + displayErrorContext(rule, element); + return false; + } + } + } + + return true; + } + + function hasXhrLabel(element) { + let xhr = element.querySelector(".xhr"); + if (!xhr) { + return false; + } + return true; + } + + function checkMessage(rule, element) { + let elemText = element.textContent; + + if (rule.text && !checkText(rule.text, elemText)) { + return false; + } + + if (rule.noText && checkText(rule.noText, elemText)) { + return false; + } + + if (rule.consoleTable && !checkConsoleTable(rule, element)) { + return false; + } + + if (rule.consoleTrace && !checkConsoleTrace(rule, element)) { + return false; + } + + if (rule.consoleTime && !checkConsoleTime(rule, element)) { + return false; + } + + if (rule.consoleTimeEnd && !checkConsoleTimeEnd(rule, element)) { + return false; + } + + if (rule.consoleDir && !checkConsoleDir(rule, element)) { + return false; + } + + if (rule.consoleGroup && !checkConsoleGroup(rule, element)) { + return false; + } + + if (rule.source && !checkSource(rule, element)) { + return false; + } + + if ("collapsible" in rule && !checkCollapsible(rule, element)) { + return false; + } + + if (rule.isXhr && !hasXhrLabel(element)) { + return false; + } + + if (!rule.isXhr && hasXhrLabel(element)) { + return false; + } + + let partialMatch = !!(rule.consoleTrace || rule.consoleTime || + rule.consoleTimeEnd); + + // The rule tries to match the newer types of messages, based on their + // object constructor. + if (rule.type) { + if (!element._messageObject || + !(element._messageObject instanceof rule.type)) { + if (partialMatch) { + ok(false, "message type for rule: " + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + partialMatch = true; + } + + if ("category" in rule && element.category != rule.category) { + if (partialMatch) { + is(element.category, rule.category, + "message category for rule: " + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + + if ("severity" in rule && element.severity != rule.severity) { + if (partialMatch) { + is(element.severity, rule.severity, + "message severity for rule: " + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + + if (rule.text) { + partialMatch = true; + } + + if (rule.stacktrace && !checkStacktrace(rule, element)) { + if (partialMatch) { + ok(false, "failed to match stacktrace for rule: " + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + + if (rule.category == CATEGORY_NETWORK && "url" in rule && + !checkText(rule.url, element.url)) { + return false; + } + + if ("repeats" in rule) { + let repeats = element.querySelector(".message-repeats"); + if (!repeats || repeats.getAttribute("value") != rule.repeats) { + return false; + } + } + + if ("groupDepth" in rule) { + let indentNode = element.querySelector(".indent"); + let indent = (GROUP_INDENT * rule.groupDepth) + "px"; + if (!indentNode || indentNode.style.width != indent) { + is(indentNode.style.width, indent, + "group depth check failed for message rule: " + displayRule(rule)); + return false; + } + } + + if ("longString" in rule) { + let longStrings = element.querySelectorAll(".longStringEllipsis"); + if (rule.longString != !!longStrings[0]) { + if (partialMatch) { + is(!!longStrings[0], rule.longString, + "long string existence check failed for message rule: " + + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + rule.longStrings = longStrings; + } + + if ("objects" in rule) { + let clickables = element.querySelectorAll(".message-body a"); + if (rule.objects != !!clickables[0]) { + if (partialMatch) { + is(!!clickables[0], rule.objects, + "objects existence check failed for message rule: " + + displayRule(rule)); + displayErrorContext(rule, element); + } + return false; + } + rule.clickableElements = clickables; + } + + if ("prefix" in rule) { + let prefixNode = element.querySelector(".prefix"); + is(prefixNode && prefixNode.textContent, rule.prefix, "Check prefix"); + } + + let count = rule.count || 1; + if (!rule.matched) { + rule.matched = new Set(); + } + rule.matched.add(element); + + return rule.matched.size == count; + } + + function onMessagesAdded(event, newMessages) { + for (let msg of newMessages) { + let elem = msg.node; + let location = getRenderedSource(elem); + if (location && location.url) { + let url = location.url; + // Prevent recursion with the browser console and any potential + // messages coming from head.js. + if (url.indexOf("devtools/client/webconsole/test/head.js") != -1) { + continue; + } + } + + for (let rule of rules) { + if (rule._ruleMatched) { + continue; + } + + let matched = checkMessage(rule, elem); + if (matched) { + rule._ruleMatched = true; + rulesMatched++; + ok(1, "matched rule: " + displayRule(rule)); + if (maybeDone()) { + return; + } + } + } + } + } + + function allRulesMatched() { + return options.matchCondition == "all" && rulesMatched == rules.length || + options.matchCondition == "any" && rulesMatched > 0; + } + + function maybeDone() { + if (allRulesMatched()) { + if (listenerAdded) { + webconsole.ui.off("new-messages", onMessagesAdded); + } + gPendingOutputTest--; + deferred.resolve(rules); + return true; + } + return false; + } + + function testCleanup() { + if (allRulesMatched()) { + return; + } + + if (webconsole.ui) { + webconsole.ui.off("new-messages", onMessagesAdded); + } + + for (let rule of rules) { + if (!rule._ruleMatched) { + ok(false, "failed to match rule: " + displayRule(rule)); + } + } + } + + function displayRule(rule) { + return rule.name || rule.text; + } + + function displayErrorContext(rule, element) { + console.log("error occured during rule " + displayRule(rule)); + console.log("while checking the following message"); + dumpMessageElement(element); + } + + executeSoon(() => { + let messages = []; + for (let elem of webconsole.outputNode.childNodes) { + messages.push({ + node: elem, + update: false, + }); + } + + onMessagesAdded("new-messages", messages); + + if (!allRulesMatched()) { + listenerAdded = true; + registerCleanupFunction(testCleanup); + webconsole.ui.on("new-messages", onMessagesAdded); + } + }); + + return deferred.promise; +} + +function whenDelayedStartupFinished(win, callback) { + Services.obs.addObserver(function observer(subject, topic) { + if (win == subject) { + Services.obs.removeObserver(observer, topic); + executeSoon(callback); + } + }, "browser-delayed-startup-finished", false); +} + +/** + * Check the web console output for the given inputs. Each input is checked for + * the expected JS eval result, the result of calling print(), the result of + * console.log(). The JS eval result is also checked if it opens the variables + * view on click. + * + * @param object hud + * The web console instance to work with. + * @param array inputTests + * An array of input tests. An input test element is an object. Each + * object has the following properties: + * - input: string, JS input value to execute. + * + * - output: string|RegExp, expected JS eval result. + * + * - inspectable: boolean, when true, the test runner expects the JS eval + * result is an object that can be clicked for inspection. + * + * - noClick: boolean, when true, the test runner does not click the JS + * eval result. Some objects, like |window|, have a lot of properties and + * opening vview for them is very slow (they can cause timeouts in debug + * builds). + * + * - consoleOutput: string|RegExp, optional, expected consoleOutput + * If not provided consoleOuput = output; + * + * - printOutput: string|RegExp, optional, expected output for + * |print(input)|. If this is not provided, printOutput = output. + * + * - variablesViewLabel: string|RegExp, optional, the expected variables + * view label when the object is inspected. If this is not provided, then + * |output| is used. + * + * - inspectorIcon: boolean, when true, the test runner expects the + * result widget to contain an inspectorIcon element (className + * open-inspector). + * + * - expectedTab: string, optional, the full URL of the new tab which + * must open. If this is not provided, any new tabs that open will cause + * a test failure. + */ +function checkOutputForInputs(hud, inputTests) { + let container = gBrowser.tabContainer; + + function* runner() { + for (let [i, entry] of inputTests.entries()) { + info("checkInput(" + i + "): " + entry.input); + yield checkInput(entry); + } + container = null; + } + + function* checkInput(entry) { + yield checkConsoleLog(entry); + yield checkPrintOutput(entry); + yield checkJSEval(entry); + } + + function* checkConsoleLog(entry) { + info("Logging"); + hud.jsterm.clearOutput(); + hud.jsterm.execute("console.log(" + entry.input + ")"); + + let consoleOutput = "consoleOutput" in entry ? + entry.consoleOutput : entry.output; + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "console.log() output: " + consoleOutput, + text: consoleOutput, + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }); + + let msg = [...result.matched][0]; + + if (entry.consoleLogClick) { + yield checkObjectClick(entry, msg); + } + + if (typeof entry.inspectorIcon == "boolean") { + info("Checking Inspector Link"); + yield checkLinkToInspector(entry.inspectorIcon, msg); + } + } + + function checkPrintOutput(entry) { + info("Printing"); + hud.jsterm.clearOutput(); + hud.jsterm.execute("print(" + entry.input + ")"); + + let printOutput = entry.printOutput || entry.output; + + return waitForMessages({ + webconsole: hud, + messages: [{ + name: "print() output: " + printOutput, + text: printOutput, + category: CATEGORY_OUTPUT, + }], + }); + } + + function* checkJSEval(entry) { + info("Evaluating"); + hud.jsterm.clearOutput(); + hud.jsterm.execute(entry.input); + + let evalOutput = entry.evalOutput || entry.output; + + let [result] = yield waitForMessages({ + webconsole: hud, + messages: [{ + name: "JS eval output: " + entry.evalOutput, + text: entry.evalOutput, + category: CATEGORY_OUTPUT, + }], + }); + + let msg = [...result.matched][0]; + if (!entry.noClick) { + yield checkObjectClick(entry, msg); + } + if (typeof entry.inspectorIcon == "boolean") { + info("Checking Inspector Link: " + entry.input); + yield checkLinkToInspector(entry.inspectorIcon, msg); + } + } + + function* checkObjectClick(entry, msg) { + info("Clicking"); + let body; + if (entry.getClickableNode) { + body = entry.getClickableNode(msg); + } else { + body = msg.querySelector(".message-body a") || + msg.querySelector(".message-body"); + } + ok(body, "the message body"); + + let deferredVariablesView = promise.defer(); + entry._onVariablesViewOpen = onVariablesViewOpen.bind(null, entry, + deferredVariablesView); + hud.jsterm.on("variablesview-open", entry._onVariablesViewOpen); + + let deferredTab = promise.defer(); + entry._onTabOpen = onTabOpen.bind(null, entry, deferredTab); + container.addEventListener("TabOpen", entry._onTabOpen, true); + + body.scrollIntoView(); + + if (!entry.suppressClick) { + EventUtils.synthesizeMouse(body, 2, 2, {}, hud.iframeWindow); + } + + if (entry.inspectable) { + info("message body tagName '" + body.tagName + "' className '" + + body.className + "'"); + yield deferredVariablesView.promise; + } else { + hud.jsterm.off("variablesview-open", entry._onVariablesView); + entry._onVariablesView = null; + } + + if (entry.expectedTab) { + yield deferredTab.promise; + } else { + container.removeEventListener("TabOpen", entry._onTabOpen, true); + entry._onTabOpen = null; + } + + yield promise.resolve(null); + } + + function onVariablesViewOpen(entry, {resolve, reject}, event, view, options) { + info("Variables view opened"); + let label = entry.variablesViewLabel || entry.output; + if (typeof label == "string" && options.label != label) { + return; + } + if (label instanceof RegExp && !label.test(options.label)) { + return; + } + + hud.jsterm.off("variablesview-open", entry._onVariablesViewOpen); + entry._onVariablesViewOpen = null; + ok(entry.inspectable, "variables view was shown"); + + resolve(null); + } + + function onTabOpen(entry, {resolve, reject}, event) { + container.removeEventListener("TabOpen", entry._onTabOpen, true); + entry._onTabOpen = null; + let tab = event.target; + let browser = gBrowser.getBrowserForTab(tab); + + Task.spawn(function* () { + yield loadBrowser(browser); + let uri = yield ContentTask.spawn(browser, {}, function* () { + return content.location.href; + }); + ok(entry.expectedTab && entry.expectedTab == uri, + "opened tab '" + uri + "', expected tab '" + entry.expectedTab + "'"); + yield closeTab(tab); + }).then(resolve, reject); + } + + return Task.spawn(runner); +} + +/** + * Check the web console DOM element output for the given inputs. + * Each input is checked for the expected JS eval result. The JS eval result is + * also checked if it opens the inspector with the correct node selected on + * inspector icon click + * + * @param object hud + * The web console instance to work with. + * @param array inputTests + * An array of input tests. An input test element is an object. Each + * object has the following properties: + * - input: string, JS input value to execute. + * + * - output: string, expected JS eval result. + * + * - displayName: string, expected NodeFront's displayName. + * + * - attr: Array, expected NodeFront's attributes + */ +function checkDomElementHighlightingForInputs(hud, inputs) { + function* runner() { + let toolbox = gDevTools.getToolbox(hud.target); + + // Loading the inspector panel at first, to make it possible to listen for + // new node selections + yield toolbox.selectTool("inspector"); + let inspector = toolbox.getCurrentPanel(); + yield toolbox.selectTool("webconsole"); + + info("Iterating over the test data"); + for (let data of inputs) { + let [result] = yield jsEval(data.input, {text: data.output}); + let {msg} = yield checkWidgetAndMessage(result); + yield checkNodeHighlight(toolbox, inspector, msg, data); + } + } + + function jsEval(input, message) { + info("Executing '" + input + "' in the web console"); + + hud.jsterm.clearOutput(); + hud.jsterm.execute(input); + + return waitForMessages({ + webconsole: hud, + messages: [message] + }); + } + + function* checkWidgetAndMessage(result) { + info("Getting the output ElementNode widget"); + + let msg = [...result.matched][0]; + let widget = [...msg._messageObject.widgets][0]; + ok(widget, "ElementNode widget found in the output"); + + info("Waiting for the ElementNode widget to be linked to the inspector"); + yield widget.linkToInspector(); + + return {widget, msg}; + } + + function* checkNodeHighlight(toolbox, inspector, msg, testData) { + let inspectorIcon = msg.querySelector(".open-inspector"); + ok(inspectorIcon, "Inspector icon found in the ElementNode widget"); + + info("Clicking on the inspector icon and waiting for the " + + "inspector to be selected"); + let onInspectorSelected = toolbox.once("inspector-selected"); + let onInspectorUpdated = inspector.once("inspector-updated"); + let onNewNode = toolbox.selection.once("new-node-front"); + let onNodeHighlight = toolbox.once("node-highlight"); + + EventUtils.synthesizeMouseAtCenter(inspectorIcon, {}, + inspectorIcon.ownerDocument.defaultView); + yield onInspectorSelected; + yield onInspectorUpdated; + yield onNodeHighlight; + let nodeFront = yield onNewNode; + + ok(true, "Inspector selected and new node got selected"); + + is(nodeFront.displayName, testData.displayName, + "The correct node was highlighted"); + + if (testData.attrs) { + let attrs = nodeFront.attributes; + for (let i in testData.attrs) { + is(attrs[i].name, testData.attrs[i].name, + "Expected attribute's name is present"); + is(attrs[i].value, testData.attrs[i].value, + "Expected attribute's value is present"); + } + } + + info("Unhighlight the node by moving away from the markup view"); + let onNodeUnhighlight = toolbox.once("node-unhighlight"); + let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button"); + EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"}, + inspector.toolbox.win); + yield onNodeUnhighlight; + + info("Switching back to the console"); + yield toolbox.selectTool("webconsole"); + } + + return Task.spawn(runner); +} + +/** + * Finish the request and resolve with the request object. + * + * @param {Function} predicate A predicate function that takes the request + * object as an argument and returns true if the request was the expected one, + * false otherwise. The returned promise is resolved ONLY if the predicate + * matches a request. Defaults to accepting any request. + * @return promise + * @resolves The request object. + */ +function waitForFinishedRequest(predicate = () => true) { + registerCleanupFunction(function () { + HUDService.lastFinishedRequest.callback = null; + }); + + return new Promise(resolve => { + HUDService.lastFinishedRequest.callback = request => { + // Check if this is the expected request + if (predicate(request)) { + // Match found. Clear the listener. + HUDService.lastFinishedRequest.callback = null; + + resolve(request); + } else { + info(`Ignoring unexpected request ${JSON.stringify(request, null, 2)}`); + } + }; + }); +} + +/** + * Wait for eventName on target. + * @param {Object} target An observable object that either supports on/off or + * addEventListener/removeEventListener + * @param {String} eventName + * @param {Boolean} useCapture Optional for addEventListener/removeEventListener + * @return A promise that resolves when the event has been handled + */ +function once(target, eventName, useCapture = false) { + info("Waiting for event: '" + eventName + "' on " + target + "."); + + let deferred = promise.defer(); + + for (let [add, remove] of [ + ["addEventListener", "removeEventListener"], + ["addListener", "removeListener"], + ["on", "off"] + ]) { + if ((add in target) && (remove in target)) { + target[add](eventName, function onEvent(...aArgs) { + target[remove](eventName, onEvent, useCapture); + deferred.resolve.apply(deferred, aArgs); + }, useCapture); + break; + } + } + + return deferred.promise; +} + +/** + * Checks a link to the inspector + * + * @param {boolean} hasLinkToInspector Set to true if the message should + * link to the inspector panel. + * @param {element} msg The message to test. + */ +function checkLinkToInspector(hasLinkToInspector, msg) { + let elementNodeWidget = [...msg._messageObject.widgets][0]; + if (!elementNodeWidget) { + ok(!hasLinkToInspector, "The message has no ElementNode widget"); + return true; + } + + return elementNodeWidget.linkToInspector().then(() => { + // linkToInspector resolved, check for the .open-inspector element + if (hasLinkToInspector) { + ok(msg.querySelectorAll(".open-inspector").length, + "The ElementNode widget is linked to the inspector"); + } else { + ok(!msg.querySelectorAll(".open-inspector").length, + "The ElementNode widget isn't linked to the inspector"); + } + }, () => { + // linkToInspector promise rejected, node not linked to inspector + ok(!hasLinkToInspector, + "The ElementNode widget isn't linked to the inspector"); + }); +} + +function getSourceActor(sources, URL) { + let item = sources.getItemForAttachment(a => a.source.url === URL); + return item && item.value; +} + +/** + * Make a request against an actor and resolve with the packet. + * @param object client + * The client to use when making the request. + * @param function requestType + * The client request function to run. + * @param array args + * The arguments to pass into the function. + */ +function getPacket(client, requestType, args) { + return new Promise(resolve => { + client[requestType](...args, packet => resolve(packet)); + }); +} + +/** + * Verify that clicking on a link from a popup notification message tries to + * open the expected URL. + */ +function simulateMessageLinkClick(element, expectedLink) { + let deferred = promise.defer(); + + // Invoke the click event and check if a new tab would + // open to the correct page. + let oldOpenUILinkIn = window.openUILinkIn; + window.openUILinkIn = function (link) { + if (link == expectedLink) { + ok(true, "Clicking the message link opens the desired page"); + window.openUILinkIn = oldOpenUILinkIn; + deferred.resolve(); + } + }; + + let event = new MouseEvent("click", { + detail: 1, + button: 0, + bubbles: true, + cancelable: true + }); + element.dispatchEvent(event); + + return deferred.promise; +} + +function getRenderedSource(root) { + let location = root.querySelector(".message-location .frame-link"); + return location ? { + url: location.getAttribute("data-url"), + line: location.getAttribute("data-line"), + column: location.getAttribute("data-column"), + } : null; +} diff --git a/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html b/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html new file mode 100644 index 000000000..ba5212de3 --- /dev/null +++ b/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for bug 842682 - use the debugger API for web console autocomplete</title> + <script> + var foo1 = "globalFoo"; + + var foo1Obj = { + prop1: "111", + prop2: { + prop21: "212121" + } + }; + + function firstCall() + { + var foo2 = "fooFirstCall"; + + var foo2Obj = { + prop1: { + prop11: "111111" + } + }; + + secondCall(); + } + + function secondCall() + { + var foo3 = "fooSecondCall"; + + var foo3Obj = { + prop1: { + prop11: "313131" + } + }; + + debugger; + } + </script> + </head> + <body> + <p>Hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-585956-console-trace.html b/devtools/client/webconsole/test/test-bug-585956-console-trace.html new file mode 100644 index 000000000..e658ba633 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-585956-console-trace.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html lang="en"> + <head><meta charset="utf-8"> + <title>Web Console test for bug 585956 - console.trace()</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<script type="application/javascript"> +window.foobar585956c = function(a) { + console.trace(); + return a+"c"; +}; + +function foobar585956b(a) { + return foobar585956c(a+"b"); +} + +function foobar585956a(omg) { + return foobar585956b(omg + "a"); +} + +foobar585956a("omg"); +</script> + </head> + <body> + <p>Web Console test for bug 585956 - console.trace().</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html new file mode 100644 index 000000000..ebf9c515f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>WebConsole test: iframe associated to the wrong HUD</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>WebConsole test: iframe associated to the wrong HUD.</p> + <p>This is the iframe!</p> + </body> + </html> diff --git a/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html new file mode 100644 index 000000000..8e47cf20f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>WebConsole test: iframe associated to the wrong HUD</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>WebConsole test: iframe associated to the wrong HUD.</p> + <iframe + src="http://example.com/browser/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html"></iframe> + </body> + </html> diff --git a/devtools/client/webconsole/test/test-bug-595934-canvas-css.html b/devtools/client/webconsole/test/test-bug-595934-canvas-css.html new file mode 100644 index 000000000..3c9cf03a5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-canvas-css.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: CSS Parser (with + Canvas)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript" + src="test-bug-595934-canvas-css.js"></script> + </head> + <body> + <p>Web Console test for bug 595934 - category "CSS Parser" (with + Canvas).</p> + <p><canvas width="200" height="200">Canvas support is required!</canvas></p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-595934-canvas-css.js b/devtools/client/webconsole/test/test-bug-595934-canvas-css.js new file mode 100644 index 000000000..ee1ebd425 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-canvas-css.js @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +window.addEventListener("DOMContentLoaded", function () { + var canvas = document.querySelector("canvas"); + var context = canvas.getContext("2d"); + context.strokeStyle = "foobarCanvasCssParser"; +}, false); diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.css b/devtools/client/webconsole/test/test-bug-595934-css-loader.css new file mode 100644 index 000000000..b4224430f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +body { + color: #0f0; + font-weight: bold; +} + diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^ new file mode 100644 index 000000000..e7be84a71 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^ @@ -0,0 +1 @@ +Content-Type: image/png diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.html b/devtools/client/webconsole/test/test-bug-595934-css-loader.html new file mode 100644 index 000000000..6bb0d54c5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: CSS Loader</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <link rel="stylesheet" href="test-bug-595934-css-loader.css"> + </head> + <body> + <p>Web Console test for bug 595934 - category "CSS Loader".</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-595934-css-parser.css b/devtools/client/webconsole/test/test-bug-595934-css-parser.css new file mode 100644 index 000000000..f6db82398 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-css-parser.css @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +p { + color: #0f0; + foobarCssParser: failure; +} + diff --git a/devtools/client/webconsole/test/test-bug-595934-css-parser.html b/devtools/client/webconsole/test/test-bug-595934-css-parser.html new file mode 100644 index 000000000..a4ea74ba3 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-css-parser.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: CSS Parser</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <link rel="stylesheet" type="text/css" + href="test-bug-595934-css-parser.css"> + </head> + <body> + <p>Web Console test for bug 595934 - category "CSS Parser".</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html new file mode 100644 index 000000000..a70f9011b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: DOM. + (empty getElementById())</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript" + src="test-bug-595934-empty-getelementbyid.js"></script> + </head> + <body> + <p>Web Console test for bug 595934 - category "DOM" + (empty getElementById()).</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js new file mode 100644 index 000000000..bf9ccece9 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +window.addEventListener("load", function () { + document.getElementById(""); +}, false); diff --git a/devtools/client/webconsole/test/test-bug-595934-html.html b/devtools/client/webconsole/test/test-bug-595934-html.html new file mode 100644 index 000000000..fe35afef6 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-html.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: HTML</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category "HTML".</p> + <form action="?" enctype="multipart/form-data"> + <p><label>Input <input type="text" value="test value"></label></p> + </form> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-595934-image.html b/devtools/client/webconsole/test/test-bug-595934-image.html new file mode 100644 index 000000000..312ecd49f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-image.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: Image</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category Image.</p> + <p><img src="test-bug-595934-image.jpg" alt="corrupted image"></p> + </body> +</html> + + diff --git a/devtools/client/webconsole/test/test-bug-595934-image.jpg b/devtools/client/webconsole/test/test-bug-595934-image.jpg Binary files differnew file mode 100644 index 000000000..947e5f11b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-image.jpg diff --git a/devtools/client/webconsole/test/test-bug-595934-imagemap.html b/devtools/client/webconsole/test/test-bug-595934-imagemap.html new file mode 100644 index 000000000..007c3c01b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-imagemap.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: ImageMap</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category "ImageMap".</p> + <p><img src="test-image.png" usemap="#testMap" alt="Test image"></p> + <map name="testMap"> + <area shape="rect" coords="0,0,10,10,5" href="#" alt="Test area" /> + </map> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html new file mode 100644 index 000000000..2fd8beac5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: malformed-xml. + (external file)</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"><!-- + var req = new XMLHttpRequest(); + req.open("GET", "test-bug-595934-malformedxml-external.xml", true); + req.send(null); + // --></script> + </head> + <body> + <p>Web Console test for bug 595934 - category "malformed-xml" + (external file).</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml new file mode 100644 index 000000000..4812786f1 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml @@ -0,0 +1,8 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category "malformed-xml".</p> + </body> diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml b/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml new file mode 100644 index 000000000..62689c567 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title>Web Console test for bug 595934 - category: malformed-xml</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category "malformed-xml".</p> + </body> diff --git a/devtools/client/webconsole/test/test-bug-595934-svg.xhtml b/devtools/client/webconsole/test/test-bug-595934-svg.xhtml new file mode 100644 index 000000000..572382c64 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-svg.xhtml @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <title>Web Console test for bug 595934 - category: SVG</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - category "SVG".</p> + <svg version="1.1" width="120" height="fooBarSVG" + xmlns="http://www.w3.org/2000/svg"> + <ellipse fill="#0f0" stroke="#000" cx="50%" + cy="50%" rx="50%" ry="50%" /> + </svg> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-595934-workers.html b/devtools/client/webconsole/test/test-bug-595934-workers.html new file mode 100644 index 000000000..baf5a6215 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-workers.html @@ -0,0 +1,18 @@ +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 595934 - category: DOM Worker + javascript</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p id="foobar">Web Console test for bug 595934 - category "DOM Worker + javascript".</p> + <script type="text/javascript"> + var myWorker = new Worker("test-bug-595934-workers.js"); + myWorker.postMessage("hello world"); + </script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-595934-workers.js b/devtools/client/webconsole/test/test-bug-595934-workers.js new file mode 100644 index 000000000..d23f080af --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-595934-workers.js @@ -0,0 +1,14 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* global fooBarWorker*/ +/* eslint-disable no-unused-vars*/ + +"use strict"; + +var onmessage = function () { + fooBarWorker(); +}; + diff --git a/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html new file mode 100644 index 000000000..25bdeecc5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> +<!-- + ***** BEGIN LICENSE BLOCK ***** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * Contributor(s): + * Patrick Walton <pcwalton@mozilla.com> + * + * ***** END LICENSE BLOCK ***** + --> + <title>Test for bug 597136: external script errors</title> + </head> + <body> + <h1>Test for bug 597136: external script errors</h1> + <p><button onclick="f()">Click me</button</p> + + <script type="text/javascript" + src="test-bug-597136-external-script-errors.js"></script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js new file mode 100644 index 000000000..00821e38e --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js @@ -0,0 +1,9 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function f() { + bogus.g(); +} + diff --git a/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html b/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html new file mode 100644 index 000000000..68e19e677 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Bug 597756: test error logging after tab close and reopen</title> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <h1>Bug 597756: test error logging after tab close and reopen.</h1> + + <script type="text/javascript"><!-- + fooBug597756_error.bar(); + // --></script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs b/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs new file mode 100644 index 000000000..2e78d6b7b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs @@ -0,0 +1,25 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) +{ + var Etag = '"4c881ab-b03-435f0a0f9ef00"'; + var IfNoneMatch = request.hasHeader("If-None-Match") + ? request.getHeader("If-None-Match") + : ""; + + var page = "<!DOCTYPE html><html><body><p>hello world!</p></body></html>"; + + response.setHeader("Etag", Etag, false); + + if (IfNoneMatch == Etag) { + response.setStatusLine(request.httpVersion, "304", "Not Modified"); + } + else { + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Content-Length", page.length + "", false); + response.write(page); + } +} diff --git a/devtools/client/webconsole/test/test-bug-600183-charset.html b/devtools/client/webconsole/test/test-bug-600183-charset.html new file mode 100644 index 000000000..040490a6b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-600183-charset.html @@ -0,0 +1,9 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="gb2312"> + <title>Console HTTP test page (chinese)</title> + </head> + <body> + <p>µÄÎʺò!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^ b/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^ new file mode 100644 index 000000000..9f3e2302f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^ @@ -0,0 +1 @@ +Content-Type: text/html; charset=gb2312 diff --git a/devtools/client/webconsole/test/test-bug-601177-log-levels.html b/devtools/client/webconsole/test/test-bug-601177-log-levels.html new file mode 100644 index 000000000..a59213907 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-601177-log-levels.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 601177: log levels</title> + <script src="test-bug-601177-log-levels.js" type="text/javascript"></script> + <script type="text/javascript"><!-- + window.undefinedPropertyBug601177; + // --></script> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <h1>Web Console test for bug 601177: log levels</h1> + <img src="test-image.png?bug601177"> + <img src="foobar-known-to-fail.png?bug601177"> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-601177-log-levels.js b/devtools/client/webconsole/test/test-bug-601177-log-levels.js new file mode 100644 index 000000000..afeb13ff6 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-601177-log-levels.js @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +foobarBug601177strictError = "strict error"; + +window.foobarBug601177exception(); diff --git a/devtools/client/webconsole/test/test-bug-603750-websocket.html b/devtools/client/webconsole/test/test-bug-603750-websocket.html new file mode 100644 index 000000000..f0097dd77 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-603750-websocket.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 603750 - Web Socket errors</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 595934 - Web Socket errors.</p> + <iframe src="data:text/html;charset=utf-8,hello world!"></iframe> + <script type="text/javascript" src="test-bug-603750-websocket.js"></script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-603750-websocket.js b/devtools/client/webconsole/test/test-bug-603750-websocket.js new file mode 100644 index 000000000..3d92c506b --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-603750-websocket.js @@ -0,0 +1,20 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +window.addEventListener("load", function () { + let ws1 = new WebSocket("ws://0.0.0.0:81"); + ws1.onopen = function () { + ws1.send("test 1"); + ws1.close(); + }; + + let ws2 = new window.frames[0].WebSocket("ws://0.0.0.0:82"); + ws2.onopen = function () { + ws2.send("test 2"); + ws2.close(); + }; +}, false); diff --git a/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html new file mode 100644 index 000000000..451eba21e --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>test for bug 609872 - iframe child</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>test for bug 609872 - iframe child</p> + <script>window.foobarBug609872 = 'child!';</script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html new file mode 100644 index 000000000..fdb636b97 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>test for bug 609872 - iframe parent</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>test for bug 609872 - iframe parent</p> + <script>window.foobarBug609872 = 'parent!';</script> + <iframe src="test-bug-609872-cd-iframe-child.html"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html b/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html new file mode 100644 index 000000000..edf40e80e --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>test for bug 613013</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>test for bug 613013</p> + <script type="text/javascript"><!-- + (function () { + var iframe = document.createElement('iframe'); + iframe.src = 'data:text/html;charset=utf-8,little iframe'; + document.body.appendChild(iframe); + + console.log("foobarBug613013"); + })(); + // --></script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html b/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html new file mode 100644 index 000000000..ac755e1b9 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 618078 - exception in async network request + callback</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"> + var req = new XMLHttpRequest(); + req.open('GET', 'http://example.com', true); + req.onreadystatechange = function() { + if (req.readyState == 4) { + bug618078exception(); + } + }; + req.send(null); + </script> + </head> + <body> + <p>Web Console test for bug 618078 - exception in async network request + callback.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html b/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html new file mode 100644 index 000000000..09c986703 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 621644</title> + <script> + function $(elem) { + return elem.innerHTML; + } + function $$(doc) { + return doc.title; + } + </script> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <h1>Web Console test for bug 621644</h1> + <p>hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs b/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs new file mode 100644 index 000000000..f92e0fe65 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs @@ -0,0 +1,16 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) +{ + var page = "<!DOCTYPE html><html><body><p>hello world! bug 630733</p></body></html>"; + + response.setStatusLine(request.httpVersion, "301", "Moved Permanently"); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Content-Length", page.length + "", false); + response.setHeader("x-foobar-bug630733", "bazbaz", false); + response.setHeader("Location", "/redirect-from-bug-630733", false); + response.write(page); +} diff --git a/devtools/client/webconsole/test/test-bug-632275-getters.html b/devtools/client/webconsole/test/test-bug-632275-getters.html new file mode 100644 index 000000000..349c301f3 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-632275-getters.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 632275 - getters</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + +<script type="application/javascript;version=1.8"> + document.foobar = { + _val: 5, + get val() { return ++this._val; } + }; +</script> + + </head> + <body> + <p>Web Console test for bug 632275 - getters.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html new file mode 100644 index 000000000..1eddcf350 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 632347 - iterators and generators</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<script type="application/javascript;version=1.8"> +(function(){ +function genFunc() { + var a = 5; + while (a < 10) { + yield a++; + } +} + +window._container = {}; + +_container.gen1 = genFunc(); +_container.gen1.next(); + +var obj = { foo: "bar", baz: "baaz", hay: "stack" }; +_container.iter1 = Iterator(obj); + +function Range(low, high) { + this.low = low; + this.high = high; +} + +function RangeIterator(range) { + this.range = range; + this.current = this.range.low; +} + +RangeIterator.prototype.next = function() { + if (this.current > this.range.high) { + throw StopIteration; + } else { + return this.current++; + } +} + +Range.prototype.__iterator__ = function() { + return new RangeIterator(this); +} + +_container.iter2 = new Range(3, 15); + +_container.gen2 = (function* () { for (let i in _container.iter2) yield i * 2; })(); +})(); +</script> + </head> + <body> + <p>Web Console test for bug 632347 - iterators and generators.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-644419-log-limits.html b/devtools/client/webconsole/test/test-bug-644419-log-limits.html new file mode 100644 index 000000000..21d99ba14 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-644419-log-limits.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for bug 644419: console log limits</title> + </head> + <body> + <h1>Test for bug 644419: Console should have user-settable log limits for + each message category</h1> + + <script type="text/javascript"> + function foo() { + bar.baz(); + } + foo(); + </script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-646025-console-file-location.html b/devtools/client/webconsole/test/test-bug-646025-console-file-location.html new file mode 100644 index 000000000..7c80f1446 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-646025-console-file-location.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console file location test</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script src="test-file-location.js"></script> + </head> + <body> + <h1>Web Console File Location Test Page</h1> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-658368-time-methods.html b/devtools/client/webconsole/test/test-bug-658368-time-methods.html new file mode 100644 index 000000000..cc50b6313 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-658368-time-methods.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <head> + <meta charset="utf-8"> + <title>Test for bug 658368: Expand console object with time and timeEnd + methods</title> + </head> + <body> + <h1>Test for bug 658368: Expand console object with time and timeEnd + methods</h1> + + <script type="text/javascript"> + function foo() { + console.timeEnd("aTimer"); + } + console.time("aTimer"); + foo(); + console.time("bTimer"); + </script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html b/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html new file mode 100644 index 000000000..db83274f0 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf8"> + <title>Mixed Content test - http on https</title> + <script src="testscript.js"></script> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <iframe src = "http://example.com"></iframe> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html new file mode 100644 index 000000000..ccb363ed9 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>I am sandboxed and want to escape.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html new file mode 100644 index 000000000..b9939fe83 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe +src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html new file mode 100644 index 000000000..7678d15fe --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe +src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html new file mode 100644 index 000000000..233a6cb70 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (allow-scripts, allow-same-origin)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html new file mode 100644 index 000000000..da0d58819 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (allow-scripts, no allow-same-origin)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html new file mode 100644 index 000000000..f33f0a6dc --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html @@ -0,0 +1,13 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (no allow-scripts, allow-same-origin)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-same-origin"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html new file mode 100644 index 000000000..c0ff6994a --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (allow-scripts, allow-same-origin)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe +src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html new file mode 100644 index 000000000..84e0b6c72 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (allow-scripts, allow-same-origin, nested)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe +src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html" sandbox="allow-scripts allow-same-origin"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html new file mode 100644 index 000000000..72d86931a --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html @@ -0,0 +1,14 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 752559 - print warning to error console when iframe sandbox + is being used ineffectively (nested, allow-scripts, allow-same-origin)</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <iframe +src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html new file mode 100644 index 000000000..d7bcd45d6 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html @@ -0,0 +1,28 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 762593 - Add warning/error Message to Web Console when the + page includes Insecure Password fields</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + + <!-- This test tests the scenario where a javascript adds password fields to + an about:blank iframe inside an insecure web page. It ensures that + insecure password fields like those are detected and a warning is sent to + the web console. --> + </head> + <body> + <p>This insecure page is served with an about:blank iframe. A script then adds a + password field to it.</p> + <iframe id = "myiframe" width = "300" height="300" > + </iframe> + <script> + var doc = window.document; + var myIframe = doc.getElementById("myiframe"); + myIframe.contentDocument.open(); + myIframe.contentDocument.write("<form><input type = 'password' name='pwd' value='test'> </form>"); + myIframe.contentDocument.close(); + </script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html new file mode 100644 index 000000000..f473303f4 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html @@ -0,0 +1,16 @@ +<!doctype html> +<html> + <head> + <meta charset="utf8"> + <title>Bug 762593 - Add warning/error Message to Web Console when the + page includes Insecure Password fields</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>This page is served with an iframe with insecure password field.</p> + <iframe src + ="http://example.com/browser/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html"> + </iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-766001-console-log.js b/devtools/client/webconsole/test/test-bug-766001-console-log.js new file mode 100644 index 000000000..a4be0cb15 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-766001-console-log.js @@ -0,0 +1,10 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function onLoad123() { + console.log("Blah Blah"); +} + +window.addEventListener("load", onLoad123, false); diff --git a/devtools/client/webconsole/test/test-bug-766001-js-console-links.html b/devtools/client/webconsole/test/test-bug-766001-js-console-links.html new file mode 100644 index 000000000..6a6ac6008 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-766001-js-console-links.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 766001 : Open JS/Console call Links in Debugger</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript" src="test-bug-766001-js-errors.js"></script> + <script type="text/javascript" src="test-bug-766001-console-log.js"></script> + </head> + <body> + <p>Web Console test for bug 766001 : Open JS/Console call Links in Debugger.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-766001-js-errors.js b/devtools/client/webconsole/test/test-bug-766001-js-errors.js new file mode 100644 index 000000000..85321813a --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-766001-js-errors.js @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +window.addEventListener("load", function () { + document.bar(); +}, false); diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css b/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css new file mode 100644 index 000000000..ad7fd1999 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +body { + color: #0f0; + font-weight: green; +} + diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css b/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css new file mode 100644 index 000000000..91b14137a --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css @@ -0,0 +1,10 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +body { + color: #0fl; + font-weight: bold; +} + diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors.html b/devtools/client/webconsole/test/test-bug-782653-css-errors.html new file mode 100644 index 000000000..7ca11fc34 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-782653-css-errors.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 782653 : Open CSS Links in Style Editor</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <link rel="stylesheet" href="test-bug-782653-css-errors-1.css"> + <link rel="stylesheet" href="test-bug-782653-css-errors-2.css"> + </head> + <body> + <p>Web Console test for bug 782653 : Open CSS Links in Style Editor.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-837351-security-errors.html b/devtools/client/webconsole/test/test-bug-837351-security-errors.html new file mode 100644 index 000000000..db83274f0 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-837351-security-errors.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf8"> + <title>Mixed Content test - http on https</title> + <script src="testscript.js"></script> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <iframe src = "http://example.com"></iframe> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html b/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html new file mode 100644 index 000000000..51bc0de28 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html lang="en"> + <head><meta charset="utf-8"> + <title>Web Console test for bug 859170 - very long strings hang the browser</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<script type="application/javascript"> +(function() { +var longString = "abbababazomglolztest"; +for (var i = 0; i < 10; i++) { + longString += longString + longString; +} + +longString = "foobar" + (new Array(9000)).join("a") + "foobaz" + + longString + "boom!"; +console.log(longString); +})(); +</script> + </head> + <body> + <p>Web Console test for bug 859170 - very long strings hang the browser.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-869003-iframe.html b/devtools/client/webconsole/test/test-bug-869003-iframe.html new file mode 100644 index 000000000..5a29728e5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-869003-iframe.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 869003</title> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"><!-- + window.onload = function testConsoleLogging() + { + var o = { hello: "world!", bug: 869003 }; + console.log("foobar", o); + }; + // --></script> + </head> + <body> + <p>Make sure users can inspect objects from cross-domain iframes.</p> + <p>Iframe window.</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-869003-top-window.html b/devtools/client/webconsole/test/test-bug-869003-top-window.html new file mode 100644 index 000000000..a2da438f6 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-869003-top-window.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 869003</title> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Make sure users can inspect objects from cross-domain iframes.</p> + <p>Top window.</p> + <iframe src="http://example.org/browser/devtools/client/webconsole/test/test-bug-869003-iframe.html"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html b/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html new file mode 100644 index 000000000..de297d9b5 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p> + <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p> + <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html b/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html new file mode 100644 index 000000000..54a4e9038 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>test for bug 989025 - iframe parent</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>test for bug 989025 - iframe parent</p> + <iframe src="http://mochi.test:8888/browser/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html new file mode 100644 index 000000000..912e301f0 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Click on function should point to source</title> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript" src="test-bug_1050691_click_function_to_source.js"></script> + </head> + <body></body> +</html> diff --git a/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js new file mode 100644 index 000000000..1eddf0d6e --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js @@ -0,0 +1,10 @@ +/** + * this + * is + * a + * function + */ +function foo() { + console.log(foo); +} + diff --git a/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html b/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html new file mode 100644 index 000000000..f2d650a5d --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Console test</title> + <!-- Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript" src="test-bug_923281_test1.js"></script> + <script type="text/javascript" src="test-bug_923281_test2.js"></script> + </head> + <body></body> +</html> diff --git a/devtools/client/webconsole/test/test-bug_923281_test1.js b/devtools/client/webconsole/test/test-bug_923281_test1.js new file mode 100644 index 000000000..1c07f1155 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_923281_test1.js @@ -0,0 +1,7 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +console.log("Sample log."); +console.log("This log should be filtered when filtered for test2.js."); diff --git a/devtools/client/webconsole/test/test-bug_923281_test2.js b/devtools/client/webconsole/test/test-bug_923281_test2.js new file mode 100644 index 000000000..7ac85b387 --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_923281_test2.js @@ -0,0 +1,8 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +console.log("This is a random text."); diff --git a/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html b/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html new file mode 100644 index 000000000..ab44de09f --- /dev/null +++ b/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test for bug 939783 - different console.trace() calls + wrongly filtered as duplicates</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<script type="application/javascript"> +function foo1() { + foo2(); +} + +function foo1b() { + foo2(); +} + +function foo2() { + foo3(); +} + +function foo3() { + console.trace(); +} + +foo1(); foo1(); +foo1b(); + +</script> + </head> + <body> + <p>Web Console test for bug 939783 - different console.trace() calls + wrongly filtered as duplicates</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-certificate-messages.html b/devtools/client/webconsole/test/test-certificate-messages.html new file mode 100644 index 000000000..b0419a6fc --- /dev/null +++ b/devtools/client/webconsole/test/test-certificate-messages.html @@ -0,0 +1,22 @@ +<!-- + Bug 1068949 - Log crypto warnings to the security pane in the webconsole +--> + +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf8"> + <title>Security warning test - no violations</title> + <!-- ensure no subresource errors so window re-use doesn't cause failures --> + <link rel="icon" href="data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC"> + <script> + console.log("If you haven't seen ssl warnings yet, you won't"); + </script> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-closure-optimized-out.html b/devtools/client/webconsole/test/test-closure-optimized-out.html new file mode 100644 index 000000000..3ad4e8fc0 --- /dev/null +++ b/devtools/client/webconsole/test/test-closure-optimized-out.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html> + <head> + <meta charset='utf-8'/> + <title>Debugger Test for Inspecting Optimized-Out Variables</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"> + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload); + function clickHandler(event) { + button.removeEventListener("click", clickHandler, false); + function outer(arg) { + var upvar = arg * 2; + // The inner lambda only aliases arg, so the frontend alias analysis decides + // that upvar is not aliased and is not in the CallObject. + return function () { + arg += 2; + }; + } + + var f = outer(42); + f(); + } + var button = document.querySelector("button"); + button.addEventListener("click", clickHandler, false); + }); + </script> + + </head> + <body> + <button>Click me!</button> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-closures.html b/devtools/client/webconsole/test/test-closures.html new file mode 100644 index 000000000..4fadade20 --- /dev/null +++ b/devtools/client/webconsole/test/test-closures.html @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<html> + <head> + <meta charset='utf-8'/> + <title>Console Test for Closure Inspection</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"> + function injectPerson() { + var PersonFactory = function _pfactory(name) { + var foo = 10; + return { + getName: function() { return name; }, + getFoo: function() { foo = Date.now(); return foo; } + }; + }; + window.george = new PersonFactory("George"); + debugger; + } + </script> + + </head> + <body> + <button onclick="injectPerson()">Test</button> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-api-stackframe.html b/devtools/client/webconsole/test/test-console-api-stackframe.html new file mode 100644 index 000000000..df7fef9b1 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-api-stackframe.html @@ -0,0 +1,32 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for bug 920116 - stacktraces for console API messages</title> + <script> + function firstCall() { + secondCall(); + } + + function secondCall() { + thirdCall(); + } + + function thirdCall() { + console.log("foo-log"); + console.error("foo-error"); + console.exception("foo-exception"); + console.assert("red" == "blue", "foo-assert"); + } + + window.onload = firstCall; + </script> + </head> + <body> + <p>Hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-assert.html b/devtools/client/webconsole/test/test-console-assert.html new file mode 100644 index 000000000..b104d72d4 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-assert.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> + <meta charset="utf-8"> + <title>console.assert() test</title> + <script type="text/javascript"> + function test() { + console.log("start"); + console.assert(false, "false assert"); + console.assert(0, "falsy assert"); + console.assert(true, "true assert"); + console.log("end"); + } + </script> + </head> + <body> + <p>test console.assert()</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-clear.html b/devtools/client/webconsole/test/test-console-clear.html new file mode 100644 index 000000000..8009db858 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-clear.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console.clear() tests</title> + <script type="text/javascript"> + console.log("log1"); + console.log("log2"); + console.clear(); + + window.objFromPage = { a: 1 }; + </script> + </head> + <body> + <h1 id="header">Clear Demo</h1> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-column.html b/devtools/client/webconsole/test/test-console-column.html new file mode 100644 index 000000000..ff9cc81e1 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-column.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <meta charset="utf-8"> + <title>Console test</title> + + <script type="text/javascript"> + console.info("INLINE SCRIPT:"); console.log('Further'); + console.warn("I'm warning you, he will eat up all yr bacon."); + console.error("Error Message"); + console.log('Rainbooooww'); + console.log('NYAN CATZ'); + </script> + </head> +</html> diff --git a/devtools/client/webconsole/test/test-console-count-external-file.js b/devtools/client/webconsole/test/test-console-count-external-file.js new file mode 100644 index 000000000..cca9e2f10 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-count-external-file.js @@ -0,0 +1,11 @@ +/* eslint-disable no-unused-vars */ + +"use strict"; + +function counterExternalFile() { + console.count("console.count() testcounter"); +} +function externalCountersWithoutLabel() { + console.count(); + console.count(); +} diff --git a/devtools/client/webconsole/test/test-console-count.html b/devtools/client/webconsole/test/test-console-count.html new file mode 100644 index 000000000..e6db0ebb0 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-count.html @@ -0,0 +1,56 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> + <meta charset="utf-8"> + <title>console.count() test</title> + <script src="test-console-count-external-file.js"></script> + <script tyoe="text/javascript"> + function counterSeperateScriptTag() { + console.count("console.count() testcounter"); + } + </script> + <script type="text/javascript"> + function counterNoLabel() { + console.count(); + } + function countersWithoutLabel() { + console.count(); + console.count(); + } + function counterWithLabel() { + console.count("console.count() testcounter"); + } + function testLocal() { + console.log("start"); + counterNoLabel(); + counterNoLabel(); + countersWithoutLabel(); + counterWithLabel(); + counterWithLabel(); + counterSeperateScriptTag(); + counterSeperateScriptTag(); + console.log("end"); + } + function testExternal() { + console.log("start"); + counterExternalFile(); + counterExternalFile(); + externalCountersWithoutLabel(); + console.log("end"); + } + </script> + </head> + <body> + <p>test console.count()</p> + <button id="local" onclick="testLocal();"> + test local console.count() calls + </button> + <button id="external" onclick="testExternal();"> + test external console.count() calls + </button> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-extras.html b/devtools/client/webconsole/test/test-console-extras.html new file mode 100644 index 000000000..8685b1a80 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-extras.html @@ -0,0 +1,18 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console extended API test</title> + <script type="text/javascript"> + function test() { + console.log("start"); + console.clear(); + console.log("end"); + } + </script> + </head> + <body> + <h1 id="header">Heads Up Display Demo</h1> + <button onclick="test();">Test Extended API</button> + <div id="myDiv"></div> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-02.html b/devtools/client/webconsole/test/test-console-output-02.html new file mode 100644 index 000000000..ad90f0ebf --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-02.html @@ -0,0 +1,66 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output - 02</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> + <p>hello world!</p> + <script type="text/javascript"> +function testfn1() { return 42; } + +var testobj1 = { + testfn2: function() { return 42; }, +}; + +function testfn3() { return 42; } +testfn3.displayName = "testfn3DisplayName"; + +var array1 = [1, 2, 3, "a", "b", "c", "4", "5"]; + +var array2 = ["a", document, document.body, document.body.dataset, + document.body.classList]; + +var array3 = [1, window, null, "a", "b", undefined, false, "", -Infinity, testfn3, testobj1, "foo", "bar"]; + +var array4 = new Array(5); +array4.push("test"); +array4.push(array4); + +var typedarray1 = new Int32Array([1, 287, 8651, 40983, 8754]); + +var set1 = new Set([1, 2, null, array3, "a", "b", undefined, document.head]); +set1.add(set1); + +var bunnies = new String("bunnies") +var weakset = new WeakSet([bunnies, document.head]); + +var testobj2 = {a: "b", c: "d", e: 1, f: "2"}; +testobj2.foo = testobj1; +testobj2.bar = testobj2; +Object.defineProperty(testobj2, "getterTest", { + enumerable: true, + get: function() { + return 42; + }, +}); + +var testobj3 = {a: "b", c: "d", e: 1, f: "2", g: true, h: null, i: undefined, + j: "", k: document.styleSheets, l: document.body.childNodes, + o: new Array(125), m: document.head}; + +var testobj4 = {a: "b", c: "d"}; +Object.defineProperty(testobj4, "nonEnumerable", { value: "hello world" }); + +var map1 = new Map([["a", "b"], [document.body.children, testobj2]]); +map1.set(map1, set1); + +var weakmap = new WeakMap([[bunnies, 23], [document.body.children, testobj2]]); + + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-03.html b/devtools/client/webconsole/test/test-console-output-03.html new file mode 100644 index 000000000..9dcf051a6 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-03.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output - 03</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> + <p>hello world!</p> + <script type="text/javascript"> +function testBodyClassName() { + document.body.className = "test1 tezt2"; + return document.body; +} + +function testBodyID() { + document.body.id = 'foobarid'; + return document.body; +} + +function testBodyDataset() { + document.body.dataset.preview = 'zuzu"<a>foo'; + return document.body; +} + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-04.html b/devtools/client/webconsole/test/test-console-output-04.html new file mode 100644 index 000000000..bb4345277 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-04.html @@ -0,0 +1,77 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output - 04</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> + <p>hello world!</p> + <script type="text/javascript"> +function testTextNode() { + return document.querySelector("p").childNodes[0]; +} + +function testCommentNode() { + return document.head.childNodes[5]; +} + +function testDocumentFragment() { + var frag = document.createDocumentFragment(); + + var div = document.createElement("div"); + div.id = "foo1"; + div.className = "bar"; + frag.appendChild(div); + + var span = document.createElement("span"); + span.id = "foo2"; + span.textContent = "hello world"; + div.appendChild(span); + + var div2 = document.createElement("div"); + div2.id = "foo3"; + frag.appendChild(div2); + + return frag; +} + +function testError() { + try { + window.foobar("a"); + } catch (ex) { + return ex; + } + return null; +} + +function testDOMException() { + try { + var foo = document.querySelector("foo;()bar!"); + } catch (ex) { + return ex; + } + return null; +} + +function testCSSStyleDeclaration() { + document.body.style = 'color: green; font-size: 2em'; + return document.body.style; +} + +function testStyleSheetList() { + var style = document.querySelector("style"); + if (!style) { + style = document.createElement("style"); + style.textContent = "p, div { color: blue; font-weight: bold }\n" + + "@media print { p { background-color: yellow } }"; + document.head.appendChild(style); + } + return document.styleSheets; +} + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-dom-elements.html b/devtools/client/webconsole/test/test-console-output-dom-elements.html new file mode 100644 index 000000000..5acabfa3f --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-dom-elements.html @@ -0,0 +1,91 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output - dom elements</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body class="body-class" id="body-id"> + <p some-attribute="some-value">hello world!</p> + <p id="lots-of-attributes" a b c d e f g h i j k l m n></p> + <!-- + Be sure we have a charset in our iframe's data URI, otherwise we get the following extra + console output message: + "The character encoding of a framed document was not declared. The document may appear different if viewed without the document framing it." + This wouldn't be a big deal, but when we look for a "<p>" in our `waitForMessage` helper, + this extra encoding warning line contains the data URI source, returning a message + that was unexpected + --> + <iframe src="data:text/html;charset=US-ASCII,<p>hello from iframe</p>"></iframe> + <div class="some classname here with more classnames here"></div> + <svg> + <clipPath> + <rect x="0" y="0" width="10" height="5"></rect> + </clipPath> + </svg> + <script type="text/javascript"> +function testBodyNode() { + return document.body; +} + +function testDocumentElement() { + return document.documentElement; +} + +function testLotsOfAttributes() { + return document.querySelector("#lots-of-attributes"); +} + +function testDocument() { + return document; +} + +function testNode() { + return document.querySelector("p"); +} + +function testSvgNode() { + return document.querySelector("clipPath"); +} + +function testNodeList() { + return document.querySelectorAll("body *"); +} + +function testNodeInIframe() { + return document.querySelector("iframe").contentWindow.document.querySelector("p"); +} + +function testDocumentFragment() { + var frag = document.createDocumentFragment(); + + var span = document.createElement("span"); + span.className = 'foo'; + span.dataset.lolz = 'hehe'; + + var div = document.createElement('div') + div.id = 'fragdiv'; + + frag.appendChild(span); + frag.appendChild(div); + + return frag; +} + +function testNodeInDocumentFragment() { + var frag = testDocumentFragment(); + return frag.firstChild; +} + +function testUnattachedNode() { + var p = document.createElement("p"); + p.className = "such-class"; + p.dataset.data = "such-data"; + return p; +} + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-events.html b/devtools/client/webconsole/test/test-console-output-events.html new file mode 100644 index 000000000..908a86fab --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-events.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output for DOM events</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> + <p>hello world!</p> + + <script type="text/javascript"> +function testDOMEvents() { + function eventLogger(ev) { + console.log("eventLogger", ev); + } + document.addEventListener("mousemove", eventLogger); + document.addEventListener("keypress", eventLogger); + + synthesizeMouseMove(); + synthesizeKeyPress("a", {shiftKey: true}); +} + +function synthesizeMouseMove(element) { + var mouseEvent = document.createEvent("MouseEvent"); + mouseEvent.initMouseEvent("mousemove", true, true, window, 0, 0, 0, 0, 0, + false, false, false, false, 0, null); + + document.dispatchEvent(mouseEvent); +} + +function synthesizeKeyPress(key, options) { + var keyboardEvent = document.createEvent("KeyboardEvent"); + keyboardEvent.initKeyEvent("keypress", true, true, window, false, false, + options.shiftKey, false, key.charCodeAt(0), 0); + document.dispatchEvent(keyboardEvent); +} + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-output-regexp.html b/devtools/client/webconsole/test/test-console-output-regexp.html new file mode 100644 index 000000000..e62680d90 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-output-regexp.html @@ -0,0 +1,23 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en-US"> +<head> + <meta charset="utf-8"> + <title>Test the web console output for RegExp</title> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> + <p>hello world!</p> + + <script type="text/javascript"> +Object.defineProperty(RegExp.prototype, "flags", { + get: function() { throw Error("flags called"); } +}) +Object.defineProperty(RegExp.prototype, "source", { + get: function() { throw Error("source called"); }, +}) + </script> +</body> +</html> diff --git a/devtools/client/webconsole/test/test-console-replaced-api.html b/devtools/client/webconsole/test/test-console-replaced-api.html new file mode 100644 index 000000000..2b05d023a --- /dev/null +++ b/devtools/client/webconsole/test/test-console-replaced-api.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console test replaced API</title> + </head> + <body> + <h1 id="header">Web Console Replace API Test</h1> + <script type="text/javascript"> + window.console = {log: function (msg){}, info: function (msg){}, warn: function (msg){}, error: function (msg){}}; + </script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-server-logging-array.sjs b/devtools/client/webconsole/test/test-console-server-logging-array.sjs new file mode 100644 index 000000000..bba394264 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-server-logging-array.sjs @@ -0,0 +1,32 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) +{ + var page = "<!DOCTYPE html><html>" + + "<head><meta charset='utf-8'></head>" + + "<body><p>hello world!</p></body>" + + "</html>"; + + var data = { + "version": "4.1.0", + "columns": ["log", "backtrace", "type"], + "rows":[[ + [{ "best": "Firefox", "reckless": "Chrome", "new_ie": "Safari", "new_new_ie": "Edge"}], + "C:\\src\\www\\serverlogging\\test7.php:4:1", + "" + ]], + }; + + // Put log into headers. + var value = b64EncodeUnicode(JSON.stringify(data)); + response.setHeader("X-ChromeLogger-Data", value, false); + + response.write(page); +} + +function b64EncodeUnicode(str) { + return btoa(unescape(encodeURIComponent(str))); +} diff --git a/devtools/client/webconsole/test/test-console-server-logging.sjs b/devtools/client/webconsole/test/test-console-server-logging.sjs new file mode 100644 index 000000000..7177e7185 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-server-logging.sjs @@ -0,0 +1,32 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function handleRequest(request, response) +{ + var page = "<!DOCTYPE html><html>" + + "<head><meta charset='utf-8'></head>" + + "<body><p>hello world!</p></body>" + + "</html>"; + + var data = { + "version": "4.1.0", + "columns": ["log", "backtrace", "type"], + "rows": [[ + ["values: %s %o %i %f %s","string",{"a":10,"___class_name":"Object"},123,1.12, "\u2713"], + "C:\\src\\www\\serverlogging\\test7.php:4:1", + "" + ]] + }; + + // Put log into headers. + var value = b64EncodeUnicode(JSON.stringify(data)); + response.setHeader("X-ChromeLogger-Data", value, false); + + response.write(page); +} + +function b64EncodeUnicode(str) { + return btoa(unescape(encodeURIComponent(str))); +} diff --git a/devtools/client/webconsole/test/test-console-table.html b/devtools/client/webconsole/test/test-console-table.html new file mode 100644 index 000000000..461dedcde --- /dev/null +++ b/devtools/client/webconsole/test/test-console-table.html @@ -0,0 +1,63 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for Bug 899753 - console.table support</title> + <script> + var languages1 = [ + { name: "JavaScript", fileExtension: [".js"] }, + { name: { a: "TypeScript" }, fileExtension: ".ts" }, + { name: "CoffeeScript", fileExtension: ".coffee" } + ]; + + var languages2 = { + csharp: { name: "C#", paradigm: "object-oriented" }, + fsharp: { name: "F#", paradigm: "functional" } + }; + + function Person(firstName, lastName, age) + { + this.firstName = firstName; + this.lastName = lastName; + this.age = age; + } + + var family = {}; + family.mother = new Person("Susan", "Doyle", 32); + family.father = new Person("John", "Doyle", 33); + family.daughter = new Person("Lily", "Doyle", 5); + family.son = new Person("Mike", "Doyle", 8); + + var myMap = new Map(); + + myMap.set("a string", "value associated with 'a string'"); + myMap.set(5, "value associated with 5"); + + var mySet = new Set(); + + mySet.add(1); + mySet.add(5); + mySet.add("some text"); + mySet.add(null); + mySet.add(undefined); + + // These are globals and so won't be reclaimed by the GC. + var bunnies = new String("bunnies"); + var lizards = new String("lizards"); + + var weakmap = new WeakMap(); + weakmap.set(bunnies, 23); + weakmap.set(lizards, "oh no"); + + var weakset = new WeakSet([bunnies, lizards]); + + </script> + </head> + <body> + <p>Hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-trace-async.html b/devtools/client/webconsole/test/test-console-trace-async.html new file mode 100644 index 000000000..c7b895455 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-trace-async.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html lang="en"> + <head><meta charset="utf-8"> + <title>Web Console test for bug 1200832 - console.trace() async stacks</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<script type="application/javascript"> +function inner() { + console.trace(); +} + +function time1() { + new Promise(function(resolve, reject) { + setTimeout(resolve, 10); + }).then(inner); +} + +setTimeout(time1, 10); +</script> + </head> + <body> + <p>Web Console test for bug 1200832 - console.trace() async stacks</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console-workers.html b/devtools/client/webconsole/test/test-console-workers.html new file mode 100644 index 000000000..f4b286ae5 --- /dev/null +++ b/devtools/client/webconsole/test/test-console-workers.html @@ -0,0 +1,13 @@ +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console test</title> + </head> + <body> + <script type="text/javascript"> +var sw = new SharedWorker('data:application/javascript,console.log("foo-bar-shared-worker");'); + </script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-console.html b/devtools/client/webconsole/test/test-console.html new file mode 100644 index 000000000..b294a3ba1 --- /dev/null +++ b/devtools/client/webconsole/test/test-console.html @@ -0,0 +1,34 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console test</title> + <script type="text/javascript"> + var fooObj = { + testProp: "testValue" + }; + + function test() { + var str = "Dolske Digs Bacon, Now and Forevermore." + for (var i=0; i < 5; i++) { + console.log(str); + } + } + + function testTrace() { + console.log("bug 1100562"); + console.trace(); + } + + console.info("INLINE SCRIPT:"); + test(); + console.warn("I'm warning you, he will eat up all yr bacon."); + console.error("Error Message"); + </script> + </head> + <body> + <h1 id="header">Heads Up Display Demo</h1> + <button onclick="test();">Log stuff about Dolske</button> + <button id="testTrace" onclick="testTrace();">Log stuff with stacktrace</button> + <div id="myDiv"></div> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-consoleiframes.html b/devtools/client/webconsole/test/test-consoleiframes.html new file mode 100644 index 000000000..a8176f93a --- /dev/null +++ b/devtools/client/webconsole/test/test-consoleiframes.html @@ -0,0 +1,13 @@ +<html> +<head> + <script> + console.log("main file"); + </script> +</head> +<body> +<h1>iframe console test</h1> +<iframe src="test-iframe1.html"></iframe> +<iframe src="test-iframe2.html"></iframe> +<iframe src="test-iframe3.html"></iframe> +</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-cu-reporterror.js b/devtools/client/webconsole/test/test-cu-reporterror.js new file mode 100644 index 000000000..6e2f9d262 --- /dev/null +++ b/devtools/client/webconsole/test/test-cu-reporterror.js @@ -0,0 +1,4 @@ +function a() { + Components.utils.reportError("bug1141222"); +} +a(); diff --git a/devtools/client/webconsole/test/test-data.json b/devtools/client/webconsole/test/test-data.json new file mode 100644 index 000000000..471d240b5 --- /dev/null +++ b/devtools/client/webconsole/test/test-data.json @@ -0,0 +1 @@ +{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-data.json^headers^ b/devtools/client/webconsole/test/test-data.json^headers^ new file mode 100644 index 000000000..7b5e82d4b --- /dev/null +++ b/devtools/client/webconsole/test/test-data.json^headers^ @@ -0,0 +1 @@ +Content-Type: application/json diff --git a/devtools/client/webconsole/test/test-duplicate-error.html b/devtools/client/webconsole/test/test-duplicate-error.html new file mode 100644 index 000000000..1b2691672 --- /dev/null +++ b/devtools/client/webconsole/test/test-duplicate-error.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Console duplicate error test</title> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + + See https://bugzilla.mozilla.org/show_bug.cgi?id=582201 + --> + </head> + <body> + <h1>Heads Up Display - duplicate error test</h1> + + <script type="text/javascript"><!-- + fooDuplicateError1.bar(); + // --></script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html b/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html new file mode 100644 index 000000000..cf19629f4 --- /dev/null +++ b/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html @@ -0,0 +1,7 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="ISO-8859-1"> +</head> +<body>üöä</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-error.html b/devtools/client/webconsole/test/test-error.html new file mode 100644 index 000000000..abf62a3f1 --- /dev/null +++ b/devtools/client/webconsole/test/test-error.html @@ -0,0 +1,21 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Console error test</title> + </head> + <body> + <h1>Heads Up Display - error test</h1> + <p><button>generate error</button></p> + + <script type="text/javascript"><!-- + var button = document.getElementsByTagName("button")[0]; + + button.addEventListener("click", function clicker () { + button.removeEventListener("click", clicker, false); + fooBazBaz.bar(); + }, false); + // --></script> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-eval-in-stackframe.html b/devtools/client/webconsole/test/test-eval-in-stackframe.html new file mode 100644 index 000000000..ec1bf3f30 --- /dev/null +++ b/devtools/client/webconsole/test/test-eval-in-stackframe.html @@ -0,0 +1,39 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for bug 783499 - use the debugger API in the web console</title> + <script> + var foo = "globalFooBug783499"; + var fooObj = { + testProp: "testValue", + }; + + function firstCall() + { + var foo = "fooFirstCall"; + var foo3 = "foo3FirstCall"; + secondCall(); + } + + function secondCall() + { + var foo2 = "foo2SecondCall"; + var fooObj = { + testProp2: "testValue2", + }; + var fooObj2 = { + testProp22: "testValue22", + }; + debugger; + } + </script> + </head> + <body> + <p>Hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-exception-stackframe.html b/devtools/client/webconsole/test/test-exception-stackframe.html new file mode 100644 index 000000000..0a6dea4ca --- /dev/null +++ b/devtools/client/webconsole/test/test-exception-stackframe.html @@ -0,0 +1,43 @@ +<!DOCTYPE HTML> +<html dir="ltr" lang="en"> + <head> + <meta charset="utf8"> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + <title>Test for bug 1184172 - stacktraces for exceptions</title> + <script> + function firstCall() { + secondCall(); + } + + // Check anonymous functions + var secondCall = function () { + thirdCall(); + } + + function thirdCall() { + nonExistingMethodCall(); + } + + function domAPI() { + document.querySelector("buggy;selector"); + } + + function domException() { + throw new DOMException("DOMException"); + } + window.addEventListener("load", firstCall); + window.addEventListener("load", function onLoadDomAPI() { + domAPI(); + }); + window.addEventListener("load", function onLoadDomException() { + domException(); + }); + </script> + </head> + <body> + <p>Hello world!</p> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-file-location.js b/devtools/client/webconsole/test/test-file-location.js new file mode 100644 index 000000000..d9879a356 --- /dev/null +++ b/devtools/client/webconsole/test/test-file-location.js @@ -0,0 +1,12 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +console.log("message for level log"); +console.info("message for level info"); +console.warn("message for level warn"); +console.error("message for level error"); +console.debug("message for level debug"); diff --git a/devtools/client/webconsole/test/test-filter.html b/devtools/client/webconsole/test/test-filter.html new file mode 100644 index 000000000..219177bb2 --- /dev/null +++ b/devtools/client/webconsole/test/test-filter.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console test</title> + <script type="text/javascript"> + </script> + </head> + <body> + <h1>Heads Up Display Filter Test Page</h1> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-for-of.html b/devtools/client/webconsole/test/test-for-of.html new file mode 100644 index 000000000..876010c9e --- /dev/null +++ b/devtools/client/webconsole/test/test-for-of.html @@ -0,0 +1,8 @@ +<!DOCTYPE HTML> +<html> +<meta charset="utf-8"> +<body> +<h1>a</h1> +<div><p>b</p></div> +<h2>c</h2> +<p>d</p> diff --git a/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html b/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html new file mode 100644 index 000000000..d14b5cdd7 --- /dev/null +++ b/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html @@ -0,0 +1,15 @@ +<!doctype html> +<html> + <head> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <h1>iframe 2</h1> + <p>This frame contains a password field inside a form with insecure action.</p> + <form action="http://test"> + <input type="password" name="pwd"> + </form> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html b/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html new file mode 100644 index 000000000..dde47a78e --- /dev/null +++ b/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html @@ -0,0 +1,15 @@ +<!doctype html> +<html> + <head> + <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <h1>iframe 1</h1> + <p>This frame is served with an insecure password field.</p> + <iframe src= + "http://example.com/browser/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html"> + </iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-iframe1.html b/devtools/client/webconsole/test/test-iframe1.html new file mode 100644 index 000000000..4dd4eddfe --- /dev/null +++ b/devtools/client/webconsole/test/test-iframe1.html @@ -0,0 +1,10 @@ +<html> +<head> + <script> + console.log("iframe 1"); + </script> +</head> +<body> +<h1>iframe 1</h1> +</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-iframe2.html b/devtools/client/webconsole/test/test-iframe2.html new file mode 100644 index 000000000..c15884795 --- /dev/null +++ b/devtools/client/webconsole/test/test-iframe2.html @@ -0,0 +1,11 @@ +<html> +<head> + <script> + console.log("iframe 2"); + blah; + </script> +</head> +<body> +<h1>iframe 2</h1> +</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-iframe3.html b/devtools/client/webconsole/test/test-iframe3.html new file mode 100644 index 000000000..f0df8b669 --- /dev/null +++ b/devtools/client/webconsole/test/test-iframe3.html @@ -0,0 +1,11 @@ +<html> +<head> + <script> + console.log("iframe 3"); + </script> +</head> +<body> +<h1>iframe 3</h1> +<iframe src="test-iframe1.html"></iframe> +</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test-image.png b/devtools/client/webconsole/test/test-image.png Binary files differnew file mode 100644 index 000000000..769c63634 --- /dev/null +++ b/devtools/client/webconsole/test/test-image.png diff --git a/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html b/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html new file mode 100644 index 000000000..cb8cfdaaf --- /dev/null +++ b/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html @@ -0,0 +1,21 @@ +<!-- + Bug 875456 - Log mixed content messages from the Mixed Content Blocker to the + Security Pane in the Web Console +--> + +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf8"> + <title>Mixed Content test - http on https</title> + <script src="testscript.js"></script> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <iframe src="http://example.com"></iframe> + <img src="http://example.com/tests/image/test/mochitest/blue.png"></img> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-mutation.html b/devtools/client/webconsole/test/test-mutation.html new file mode 100644 index 000000000..e80933b06 --- /dev/null +++ b/devtools/client/webconsole/test/test-mutation.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Console mutation test</title> + <script> + window.onload = function (){ + var node = document.createElement("div"); + document.body.appendChild(node); + }; + </script> + </head> + <body> + <h1>Heads Up Display DOM Mutation Test Page</h1> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-network-request.html b/devtools/client/webconsole/test/test-network-request.html new file mode 100644 index 000000000..7cb736296 --- /dev/null +++ b/devtools/client/webconsole/test/test-network-request.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>Console HTTP test page</title> + <script type="text/javascript"><!-- + function makeXhr(aMethod, aUrl, aRequestBody, aCallback) { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open(aMethod, aUrl, true); + xmlhttp.onreadystatechange = function() { + if (aCallback && xmlhttp.readyState == 4) { + aCallback(); + } + }; + xmlhttp.send(aRequestBody); + } + + function testXhrGet(aCallback) { + makeXhr('get', 'test-data.json', null, aCallback); + } + + function testXhrWarn(aCallback) { + makeXhr('get', 'http://example.com/browser/devtools/client/netmonitor/test/sjs_cors-test-server.sjs', null, aCallback); + } + + function testXhrPost(aCallback) { + makeXhr('post', 'test-data.json', "Hello world!", aCallback); + } + // --></script> + </head> + <body> + <h1>Heads Up Display HTTP Logging Testpage</h1> + <h2>This page is used to test the HTTP logging.</h2> + + <form action="https://example.com/browser/devtools/client/webconsole/test/test-network-request.html" method="post"> + <input name="name" type="text" value="foo bar"><br> + <input name="age" type="text" value="144"><br> + </form> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-network.html b/devtools/client/webconsole/test/test-network.html new file mode 100644 index 000000000..69d3422e3 --- /dev/null +++ b/devtools/client/webconsole/test/test-network.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console network test</title> + <script src="testscript.js?foo"></script> + </head> + <body> + <h1>Heads Up Display Network Test Page</h1> + <img src="test-image.png"></img> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-observe-http-ajax.html b/devtools/client/webconsole/test/test-observe-http-ajax.html new file mode 100644 index 000000000..5abcefdad --- /dev/null +++ b/devtools/client/webconsole/test/test-observe-http-ajax.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Console HTTP test page</title> + <script type="text/javascript"> + function test() { + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open('get', 'test-data.json', false); + xmlhttp.send(null); + } + </script> + </head> + <body onload="test();"> + <h1>Heads Up Display HTTP & AJAX Test Page</h1> + <h2>This page fires an ajax request so we can see the http logging of the console</h2> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-own-console.html b/devtools/client/webconsole/test/test-own-console.html new file mode 100644 index 000000000..d1d18ebc2 --- /dev/null +++ b/devtools/client/webconsole/test/test-own-console.html @@ -0,0 +1,24 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> +<head> +<meta charset="utf-8"> +<script> + var _console = { + foo: "bar" + } + + window.console = _console; + + function loadIFrame() { + var iframe = document.body.querySelector("iframe"); + iframe.addEventListener("load", function() { + iframe.removeEventListener("load", arguments.callee, true); + }, true); + + iframe.setAttribute("src", "test-console.html"); + } +</script> +</head> +<body> + <iframe></iframe> +</body> diff --git a/devtools/client/webconsole/test/test-property-provider.html b/devtools/client/webconsole/test/test-property-provider.html new file mode 100644 index 000000000..532b00f44 --- /dev/null +++ b/devtools/client/webconsole/test/test-property-provider.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"><head> + <meta charset="utf-8"> + <title>Property provider test</title> + <script> + var testObj = { + testProp: 'testValue' + }; + </script> + </head> + <body> + <h1>Heads Up Property Provider Test Page</h1> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-repeated-messages.html b/devtools/client/webconsole/test/test-repeated-messages.html new file mode 100644 index 000000000..b19c9485e --- /dev/null +++ b/devtools/client/webconsole/test/test-repeated-messages.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf8"> + <title>Test for bugs 720180, 800510, 865288 and 1218089</title> + <script> + function testConsole() { + // same line and column number + for(var i = 0; i < 2; i++) { + console.log("foo repeat"); + } + console.log("foo repeat"); + console.error("foo repeat"); + } + function testConsoleObjects() { + for (var i = 0; i < 3; i++) { + var o = { id: "abba" + i }; + console.log("abba", o); + } + } + function testConsoleFalsyValues(){ + [NaN, undefined, null].forEach(function(item, index){ + console.log(item); + }); + [NaN, NaN].forEach(function(item, index){ + console.log(item); + }); + [undefined, undefined].forEach(function(item, index){ + console.log(item); + }); + [null, null].forEach(function(item, index){ + console.log(item); + }); + } + </script> + <style> + body { + background-image: foobarz; + } + p { + background-image: foobarz; + } + </style> + <!-- + - Any copyright is dedicated to the Public Domain. + - http://creativecommons.org/publicdomain/zero/1.0/ + --> + </head> + <body> + <p>Hello world!</p> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test-result-format-as-string.html b/devtools/client/webconsole/test/test-result-format-as-string.html new file mode 100644 index 000000000..c3ab78ee7 --- /dev/null +++ b/devtools/client/webconsole/test/test-result-format-as-string.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Web Console test: jsterm eval format as a string</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + </head> + <body> + <p>Make sure js eval results are formatted as strings.</p> + <script> + document.querySelector("p").toSource = function() { + var element = document.createElement("div"); + element.id = "foobar"; + element.textContent = "bug772506_content"; + element.setAttribute("onmousemove", + "(function () {" + + " gBrowser._bug772506 = 'foobar';" + + "})();" + ); + return element; + }; + </script> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html b/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html new file mode 100644 index 000000000..17f0e459e --- /dev/null +++ b/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html @@ -0,0 +1,12 @@ +<!DOCTYPE HTML> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf8"> + </head> + <body> + <iframe src="http://tracking.example.com/"></iframe> + </body> +</html> diff --git a/devtools/client/webconsole/test/test-webconsole-error-observer.html b/devtools/client/webconsole/test/test-webconsole-error-observer.html new file mode 100644 index 000000000..8466bc6f2 --- /dev/null +++ b/devtools/client/webconsole/test/test-webconsole-error-observer.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html dir="ltr" xml:lang="en-US" lang="en-US"> + <head> + <meta charset="utf-8"> + <title>WebConsoleErrorObserver test - bug 611032</title> + <!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> + <script type="text/javascript"> + console.log("log Bazzle"); + console.info("info Bazzle"); + console.warn("warn Bazzle"); + console.error("error Bazzle"); + + var foo = {}; + foo.bazBug611032(); + </script> + <style type="text/css"> + .foo { color: cssColorBug611032; } + </style> + </head> + <body> + <h1>WebConsoleErrorObserver test</h1> + </body> +</html> + diff --git a/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html new file mode 100644 index 000000000..bf63601bf --- /dev/null +++ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html @@ -0,0 +1,10 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="UTF-8"> + <title>Bug 1045902 - CSP: Log console message for ‘reflected-xss’</title> +</head> +<body> +Bug 1045902 - CSP: Log console message for ‘reflected-xss’ +</body> +</html> diff --git a/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^ new file mode 100644 index 000000000..0b234f0e8 --- /dev/null +++ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: reflected-xss filter; diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html new file mode 100644 index 000000000..ebb7773cb --- /dev/null +++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="UTF-8"> + <title>Bug 1092055 - Log console messages for non-top-level security errors</title> + <script src="test_bug1092055_shouldwarn.js"></script> + <!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ + --> +</head> +<body> +Bug 1092055 - Log console messages for non-top-level security errors +</body> +</html> diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js new file mode 100644 index 000000000..c7d5cec14 --- /dev/null +++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js @@ -0,0 +1,2 @@ +// It doesn't matter what this script does, but the broken HSTS header sent +// with it should result in warnings in the webconsole diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^ new file mode 100644 index 000000000..f99377fc6 --- /dev/null +++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^ @@ -0,0 +1 @@ +Strict-Transport-Security: some complete nonsense diff --git a/devtools/client/webconsole/test/test_bug_1010953_cspro.html b/devtools/client/webconsole/test/test_bug_1010953_cspro.html new file mode 100644 index 000000000..83ac6391f --- /dev/null +++ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html @@ -0,0 +1,20 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="UTF-8"> + <title>Test for Bug 1010953 - Verify that CSP and CSPRO log different console +messages.</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1010953">Mozilla Bug 1010953</a> + + +<!-- this script file allowed by the CSP header (but not by the report-only header) --> +<script src="http://some.example.com/test_bug_1010953_cspro.js"></script> + +<!-- this image allowed only be the CSP report-only header. --> +<img src="http://some.example.com/test.png"> +</body> +</html>
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^ new file mode 100644 index 000000000..03056e2cb --- /dev/null +++ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^ @@ -0,0 +1,2 @@ +Content-Security-Policy: default-src 'self'; img-src 'self'; script-src some.example.com; +Content-Security-Policy-Report-Only: default-src 'self'; img-src some.example.com; script-src 'self'; report-uri https://example.com/ignored/;
\ No newline at end of file diff --git a/devtools/client/webconsole/test/test_bug_1247459_violation.html b/devtools/client/webconsole/test/test_bug_1247459_violation.html new file mode 100644 index 000000000..fdda4eb26 --- /dev/null +++ b/devtools/client/webconsole/test/test_bug_1247459_violation.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta http-equiv="Content-Security-Policy" content="img-src https://example.com"></meta> + <meta http-equiv="Content-Security-Policy" content="img-src https://example.com"></meta> + <meta charset="UTF-8"> + <title>Test for Bug 1247459 - policy violations for header and META are displayed separately</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247459">Mozilla Bug 1247459</a> +<img src="http://some.example.com/test.png"> +</body> +</html> diff --git a/devtools/client/webconsole/test/test_bug_770099_violation.html b/devtools/client/webconsole/test/test_bug_770099_violation.html new file mode 100644 index 000000000..ccbded87a --- /dev/null +++ b/devtools/client/webconsole/test/test_bug_770099_violation.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="UTF-8"> + <title>Test for Bug 770099 - policy violation</title> +<!-- Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ --> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a> +<img src="http://some.example.com/test.png"> +</body> +</html> diff --git a/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^ b/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^ new file mode 100644 index 000000000..4c6fa3c26 --- /dev/null +++ b/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self' diff --git a/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs b/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs new file mode 100644 index 000000000..cd0e18523 --- /dev/null +++ b/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + + let issue; + switch (request.queryString) { + case "badSyntax": + response.setHeader("Public-Key-Pins", "\""); + issue = "is not syntactically correct."; + break; + case "noMaxAge": + response.setHeader("Public-Key-Pins", "max-age444"); + issue = "does not include a max-age directive."; + break; + case "invalidIncludeSubDomains": + response.setHeader("Public-Key-Pins", "includeSubDomains=abc"); + issue = "includes an invalid includeSubDomains directive."; + break; + case "invalidMaxAge": + response.setHeader("Public-Key-Pins", "max-age=abc"); + issue = "includes an invalid max-age directive."; + break; + case "multipleIncludeSubDomains": + response.setHeader("Public-Key-Pins", + "includeSubDomains; includeSubDomains"); + issue = "includes multiple includeSubDomains directives."; + break; + case "multipleMaxAge": + response.setHeader("Public-Key-Pins", + "max-age=444; max-age=999"); + issue = "includes multiple max-age directives."; + break; + case "multipleReportURIs": + response.setHeader("Public-Key-Pins", + 'report-uri="http://example.com"; ' + + 'report-uri="http://example.com"'); + issue = "includes multiple report-uri directives."; + break; + case "pinsetDoesNotMatch": + response.setHeader( + "Public-Key-Pins", + 'max-age=999; ' + + 'pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; ' + + 'pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="'); + issue = "does not include a matching pin."; + break; + } + + response.write("This page is served with a PKP header that " + issue); +} diff --git a/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs b/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs new file mode 100644 index 000000000..9e3ea7624 --- /dev/null +++ b/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs @@ -0,0 +1,39 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function handleRequest(request, response) +{ + response.setHeader("Content-Type", "text/plain; charset=utf-8", false); + + let issue; + switch (request.queryString) { + case "badSyntax": + response.setHeader("Strict-Transport-Security", "\""); + issue = "is not syntactically correct."; + break; + case "noMaxAge": + response.setHeader("Strict-Transport-Security", "max-age444"); + issue = "does not include a max-age directive."; + break; + case "invalidIncludeSubDomains": + response.setHeader("Strict-Transport-Security", "includeSubDomains=abc"); + issue = "includes an invalid includeSubDomains directive."; + break; + case "invalidMaxAge": + response.setHeader("Strict-Transport-Security", "max-age=abc"); + issue = "includes an invalid max-age directive."; + break; + case "multipleIncludeSubDomains": + response.setHeader("Strict-Transport-Security", + "includeSubDomains; includeSubDomains"); + issue = "includes multiple includeSubDomains directives."; + break; + case "multipleMaxAge": + response.setHeader("Strict-Transport-Security", + "max-age=444; max-age=999"); + issue = "includes multiple max-age directives."; + break; + } + + response.write("This page is served with a STS header that " + issue); +} diff --git a/devtools/client/webconsole/test/testscript.js b/devtools/client/webconsole/test/testscript.js new file mode 100644 index 000000000..849b03d86 --- /dev/null +++ b/devtools/client/webconsole/test/testscript.js @@ -0,0 +1,2 @@ +"use strict"; +console.log("running network console logging tests"); |