diff --git a/data/issues/console-log-hmr/main-view-model.js b/data/issues/console-log-hmr/main-view-model.js new file mode 100644 index 00000000..e1e4a53d --- /dev/null +++ b/data/issues/console-log-hmr/main-view-model.js @@ -0,0 +1,25 @@ +var Observable = require("tns-core-modules/data/observable").Observable; + +function getMessage(counter) { + if (counter <= 0) { + return "Hoorraaay! You unlocked the NativeScript clicker achievement!"; + } else { + return counter + " taps left"; + } +} + +function createViewModel() { + var viewModel = new Observable(); + viewModel.counter = 42; + viewModel.message = getMessage(viewModel.counter); + console.log("LOG Hello"); + + viewModel.onTap = function() { + this.counter--; + this.set("message", getMessage(this.counter)); + } + + return viewModel; +} + +exports.createViewModel = createViewModel; \ No newline at end of file diff --git a/tests/hmr/__init__.py b/tests/hmr/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/hmr/hello_world_js_hmr_android.py b/tests/hmr/hello_world_js_hmr_android.py new file mode 100644 index 00000000..e0d58363 --- /dev/null +++ b/tests/hmr/hello_world_js_hmr_android.py @@ -0,0 +1,128 @@ +import os +import unittest + +from core.base_class.BaseClass import BaseClass +from core.device.device import Device +from core.device.emulator import Emulator +from core.device.helpers.adb import Adb +from core.osutils.file import File +from core.settings.settings import EMULATOR_ID, ANDROID_PACKAGE, WEBPACK_PACKAGE, EMULATOR_NAME +from core.tns.replace_helper import ReplaceHelper +from core.tns.tns import Tns +from core.tns.tns_platform_type import Platform +from tests.hmr.helpers_hmr import HelpersHMR +from tests.webpack.helpers.helpers import Helpers +# import hashlib +# import re + + +class HelloWorldJSHMRAndroid(BaseClass): + + + @classmethod + def setUpClass(cls): + BaseClass.setUpClass(cls.__name__) + Emulator.stop() + Emulator.ensure_available() + + Tns.create_app(cls.app_name, update_modules=True) + Tns.install_npm(package=WEBPACK_PACKAGE, option='--save-dev', folder=cls.app_name) + Tns.platform_add_android(attributes={"--path": cls.app_name, "--frameworkPath": ANDROID_PACKAGE}) + + def setUp(self): + Tns.kill() + Helpers.emulator_cleanup(app_name=self.app_name) + BaseClass.setUp(self) + + def tearDown(self): + Tns.kill() + BaseClass.tearDown(self) + + @classmethod + def tearDownClass(cls): + BaseClass.tearDownClass() + + def test_001_android_run_hmr(self): + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr, not_existing_string_list=HelpersHMR.errors_hmr, + timeout=240) + Helpers.android_screen_match(image=HelpersHMR.image_original, timeout=120) + + HelpersHMR.apply_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) + HelpersHMR.revert_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) + + def test_002_android_run_hmr_uninstall_app(self): + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr_with_platforms, not_existing_string_list=HelpersHMR.errors_hmr, + timeout=240) + Helpers.android_screen_match(image=HelpersHMR.image_original, timeout=120) + + HelpersHMR.apply_changes_js(app_name=self.app_name, log=log, platform=Platform.ANDROID) + + # Uninstall app while `tns run` is running + Device.uninstall_app(app_prefix='org.nativescript.', platform=Platform.ANDROID) + + HelpersHMR.revert_changes_js(app_name=self.app_name, log=log, platform=Platform.ANDROID) + Helpers.android_screen_match(image=HelpersHMR.image_original, timeout=120) + + @unittest.skip("https://github.com/NativeScript/nativescript-cli/issues/4123") + def test_003_android_run_hmr_wrong_xml(self): + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr, + not_existing_string_list=HelpersHMR.errors_hmr, timeout=240) + + # Verify app looks correct inside emulator + Device.screen_match(device_name=EMULATOR_NAME, device_id=EMULATOR_ID, + expected_image=HelpersHMR.image_original) + + # Break the app with invalid xml changes + ReplaceHelper.replace(self.app_name, ReplaceHelper.CHANGE_XML_INVALID_SYNTAX) + + # Verify console notify user for broken xml + # strings = ['for activity org.nativescript.TestApp / com.tns.ErrorReportActivity'] + strings = ['com.tns.NativeScriptException', 'Parsing XML at', 'Successfully synced application', EMULATOR_ID] + Tns.wait_for_log(log_file=log, string_list=strings, timeout=120, check_interval=10) + assert Adb.wait_for_text(device_id=EMULATOR_ID, text="Exception", timeout=30), "Error activity not found!" + + # Revert changes + ReplaceHelper.rollback(self.app_name, ReplaceHelper.CHANGE_XML_INVALID_SYNTAX) + strings = ['JS: HMR: Sync...', 'JS: HMR: Hot Module Replacement Enabled. Waiting for signal.', + 'Successfully synced application', EMULATOR_ID] + Tns.wait_for_log(log_file=log, string_list=strings, timeout=120, check_interval=10) + + # Verify app looks correct inside emulator + Device.screen_match(device_name=EMULATOR_NAME, device_id=EMULATOR_ID, + expected_image=HelpersHMR.image_original) + + def test_008_android_run_hmr_console_log(self): + source_js = os.path.join('data', "issues", 'console-log-hmr', 'main-view-model.js') + target_js = os.path.join(self.app_name, 'app', 'main-view-model.js') + File.copy(src=source_js, dest=target_js) + + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + + strings = ['LOG Hello'] + Tns.wait_for_log(log_file=log, string_list=strings, timeout=120, check_interval=10) + + # Verify app looks correct inside emulator + Device.screen_match(device_name=EMULATOR_NAME, device_id=EMULATOR_ID, + expected_image=HelpersHMR.image_original) + + @unittest.skip("Don't clear behavior") + def test_009_android_run_hmr_delete_file(self): + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.wp_run, not_existing_string_list=HelpersHMR.wp_errors, + timeout=240) + HelpersHMR.android_screen_match(image=self.image_original, timeout=120) + File.remove(self.app_name + 'app', 'main-view-model.js') + + self.apply_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) + self.revert_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) diff --git a/tests/hmr/hello_world_js_hmr_ios.py b/tests/hmr/hello_world_js_hmr_ios.py new file mode 100644 index 00000000..69b1b4fb --- /dev/null +++ b/tests/hmr/hello_world_js_hmr_ios.py @@ -0,0 +1,114 @@ +import os +import unittest + +from core.base_class.BaseClass import BaseClass +from core.device.simulator import Simulator +from core.osutils.file import File +from core.settings.settings import IOS_PACKAGE, SIMULATOR_NAME, WEBPACK_PACKAGE, EMULATOR_ID +from core.tns.tns import Tns +from core.tns.tns_platform_type import Platform +from tests.hmr.helpers_hmr import HelpersHMR +from tests.webpack.helpers.helpers import Helpers +# import hashlib +# import re + +class HelloWorldJSHMRIOS(BaseClass): + + + @classmethod + def setUpClass(cls): + BaseClass.setUpClass(cls.__name__) + Simulator.stop() + cls.SIMULATOR_ID = Simulator.ensure_available(simulator_name=SIMULATOR_NAME) + + Tns.create_app(cls.app_name, update_modules=True) + Tns.install_npm(package=WEBPACK_PACKAGE, option='--save-dev', folder=cls.app_name) + Tns.platform_add_ios(attributes={'--path': cls.app_name, '--frameworkPath': IOS_PACKAGE}) + + def setUp(self): + Tns.kill() + BaseClass.setUp(self) + + def tearDown(self): + Tns.kill() + BaseClass.tearDown(self) + + @classmethod + def tearDownClass(cls): + BaseClass.tearDownClass() + + def test_001_ios_run_hmr(self): + log = Tns.run_ios(attributes={'--path': self.app_name, '--emulator': '', '--hmr': ''}, wait=False, + assert_success=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr, not_existing_string_list=HelpersHMR.errors_hmr, + timeout=240) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + Helpers.wait_webpack_watcher() + + HelpersHMR.apply_changes(app_name=self.app_name, log=log, platform=Platform.IOS) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_change, + timeout=120) + HelpersHMR.revert_changes(app_name=self.app_name, log=log, platform=Platform.IOS) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, + timeout=120) + + def test_002_ios_run_hmr_uninstall_app(self): + log = Tns.run_ios(attributes={'--path': self.app_name, '--emulator': '', '--hmr': ''}, wait=False, + assert_success=False) + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr_with_platforms, not_existing_string_list=HelpersHMR.errors_hmr, + timeout=240) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + Helpers.wait_webpack_watcher() + + HelpersHMR.apply_changes_js(app_name=self.app_name, log=log, platform=Platform.IOS) + + # Uninstall app while `tns run` is running + Simulator.uninstall("org.nativescript." + self.app_name) + + HelpersHMR.revert_changes_js(app_name=self.app_name, log=log, platform=Platform.IOS) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + Helpers.wait_webpack_watcher() + + @unittest.skip("https://github.com/NativeScript/nativescript-cli/issues/4123") + def test_003_ios_run_hmr_wrong_xml(self): + log = Tns.run_ios(attributes={'--path': self.app_name, '--emulator': '', '--hmr': ''}, wait=False, + assert_success=False) + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.run_hmr_with_platforms, not_existing_string_list=HelpersHMR.errors_hmr, + timeout=240) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + Helpers.wait_webpack_watcher() + + HelpersHMR.apply_changes_js(app_name=self.app_name, log=log, platform=Platform.IOS) + + # Uninstall app while `tns run` is running + Simulator.uninstall("org.nativescript." + self.app_name) + + HelpersHMR.revert_changes_js(app_name=self.app_name, log=log, platform=Platform.IOS) + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + Helpers.wait_webpack_watcher() + + def test_008_ios_run_hmr_console_log(self): + source_js = os.path.join('data', "issues", 'console-log-hmr', 'main-view-model.js') + target_js = os.path.join(self.app_name, 'app', 'main-view-model.js') + File.copy(src=source_js, dest=target_js) + + log = Tns.run_ios(attributes={'--path': self.app_name, '--emulator': '', '--hmr': ''}, wait=False, + assert_success=False) + strings = ['LOG Hello'] + Tns.wait_for_log(log_file=log, string_list=strings) + + Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, timeout=120) + + @unittest.skip("Don't clear behavior") + def test_004_android_run_hmr_delete_file(self): + log = Tns.run_android(attributes={'--path': self.app_name, '--device': EMULATOR_ID, '--hmr': ''}, wait=False, + assert_success=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.wp_run, not_existing_string_list=HelpersHMR.wp_errors, + timeout=240) + HelpersHMR.android_screen_match(image=self.image_original, timeout=120) + File.remove(self.app_name + 'app', 'main-view-model.js') + + self.apply_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) + self.revert_changes(app_name=self.app_name, log=log, platform=Platform.ANDROID) \ No newline at end of file diff --git a/tests/hmr/helpers_hmr.py b/tests/hmr/helpers_hmr.py new file mode 100644 index 00000000..e0dcc40a --- /dev/null +++ b/tests/hmr/helpers_hmr.py @@ -0,0 +1,163 @@ +import csv +import os +import time + +from core.device.device import Device +from core.device.helpers.adb import Adb +from core.osutils.file import File +from core.osutils.os_type import OSType +from core.osutils.process import Process +from core.settings.settings import EMULATOR_ID, EMULATOR_NAME, SIMULATOR_NAME, TEST_RUN_HOME, CURRENT_OS +from core.tns.tns import Tns +from core.tns.replace_helper import ReplaceHelper +from core.tns.tns_platform_type import Platform +from tests.webpack.helpers.helpers import Helpers + + +class HelpersHMR(object): + no_wp_run = ['Successfully installed'] + no_wp_sync = ['Successfully synced application'] + wp = ['Webpack compilation complete'] + run_hmr = ['Webpack compilation complete', 'Successfully installed', 'HMR: Sync...', + 'HMR: Hot Module Replacement Enabled. Waiting for signal.'] + run_hmr_with_platforms = ['Webpack compilation complete', 'HMR: Sync...', + 'HMR: Hot Module Replacement Enabled. Waiting for signal.', 'Successfully synced application', + 'Refreshing application on device'] + sync_hmr = ['Webpack compilation complete', 'Successfully synced application', + 'HMR: Checking for updates to the bundle.'] + errors_hmr = ['Module build failed', 'ENOENT'] + + wp_run = ['Webpack compilation complete', 'Successfully installed'] + wp_sync = ['Webpack compilation complete', 'Successfully synced application'] + wp_sync_snapshot = ['Webpack compilation complete', 'Successfully synced app', 'Stripping the snapshot flag.'] + wp_errors = ['Module not found', 'Snapshot generation failed', 'ENOENT'] + + SIMULATOR_ID = "" + + image_original = 'hello-world-js' + image_change = 'hello-world-js-js-css-xml' + + xml_change = ['app/main-page.xml', 'TAP', 'TEST'] + js_change = ['app/main-view-model.js', 'taps', 'clicks'] + css_change = ['app/app.css', '18', '32'] + + @staticmethod + def apply_changes(app_name, log, platform): + + not_found_list = [] + # Change JS, XML and CSS + ReplaceHelper.replace(app_name, HelpersHMR.js_change, sleep=10) + strings = ['HMR: The following modules were updated:', './main-view-model.js', './main-page.js', + 'Successfully transferred bundle.', + 'HMR: Successfully applied update with hmr hash '] + # strings = ['JS: HMR: The following modules were updated:', './main-view-model.js', './main-page.js', + # 'Successfully transferred bundle.{0}.hot-update.js'.format(hash()), + # 'JS: HMR: Successfully applied update with hmr hash {0}'.format(hashlib.sha1)] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='42 clicks left', timeout=20) + assert text_changed, 'Changes in JS file not applied (UI is not refreshed).' + + ReplaceHelper.replace(app_name, HelpersHMR.xml_change, sleep=10) + strings = ['Refreshing application on device', 'HMR: Checking for updates to the bundle with hmr hash', + './main-page.xml', 'HMR: Successfully applied update with hmr hash'] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='TEST') + assert text_changed, 'Changes in XML file not applied (UI is not refreshed).' + + ReplaceHelper.replace(app_name, HelpersHMR.css_change, sleep=10) + if platform == Platform.ANDROID: + Tns.wait_for_log(log_file=log, string_list=['app.css'], clean_log=False) + + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.wp_sync, not_existing_string_list=HelpersHMR.wp_errors, + timeout=120) + + # Verify application looks correct + if platform == Platform.ANDROID: + Helpers.android_screen_match(image=HelpersHMR.image_change, timeout=120) + # if platform == Platform.IOS: + # Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_change, + # timeout=120) + + @staticmethod + def apply_changes_js(app_name, log, platform): + # Change JS + ReplaceHelper.replace(app_name, HelpersHMR.js_change, sleep=10) + strings = ['HMR: The following modules were updated:', './main-view-model.js', './main-page.js', + 'Successfully transferred bundle.', + 'HMR: Successfully applied update with hmr hash '] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='42 clicks left', timeout=20) + assert text_changed, 'Changes in JS file not applied (UI is not refreshed).' + + @staticmethod + def apply_changes_xml(app_name, log, platform): + # Change XML after uninstall app from device + ReplaceHelper.replace(app_name, HelpersHMR.xml_change, sleep=10) + strings = ['Refreshing application on device', + 'HMR: Sync...','JS: HMR: Hot Module Replacement Enabled. Waiting for signal.'] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='TEST') + assert text_changed, 'Changes in XML file not applied (UI is not refreshed).' + + @staticmethod + def revert_changes_js(app_name, log, platform): + # Change JS + ReplaceHelper.rollback(app_name, HelpersHMR.js_change, sleep=10) + strings = ['Refreshing application on device', + 'HMR: Sync...','HMR: Hot Module Replacement Enabled. Waiting for signal.'] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='42 taps left', timeout=20) + assert text_changed, 'Changes in JS file not applied (UI is not refreshed)' + + @staticmethod + def revert_changes_xml(app_name, log, platform): + # Change XML after uninstall app from device + ReplaceHelper.rollback(app_name, HelpersHMR.xml_change, sleep=10) + strings = ['HMR: Sync...', 'Refreshing application on device', + 'HMR: Checking for updates to the bundle with hmr hash'] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='TAP') + assert text_changed, 'Changes in XML file not applied (UI is not refreshed).' + + @staticmethod + def revert_changes(app_name, log, platform): + # Clean old logs + if CURRENT_OS is not OSType.WINDOWS: + File.write(file_path=log, text="") + + # Revert XML changes + ReplaceHelper.rollback(app_name, HelpersHMR.xml_change, sleep=10) + strings = ['HMR: Sync...', 'Refreshing application on device', './main-page.xml', + 'HMR: Checking for updates to the bundle with hmr hash'] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='TAP') + assert text_changed, 'Changes in XML file not applied (UI is not refreshed).' + + # Revert JS changes + ReplaceHelper.rollback(app_name, HelpersHMR.js_change, sleep=10) + strings = ['HMR: The following modules were updated:', './main-view-model.js', './main-page.js', + 'Successfully transferred bundle.', 'HMR: Successfully applied update with hmr hash '] + Tns.wait_for_log(log_file=log, string_list=strings) + if platform == Platform.ANDROID: + text_changed = Device.wait_for_text(device_id=EMULATOR_ID, text='42 taps left', timeout=20) + assert text_changed, 'HMR: The following modules were updated:' + + # Revert CSS changes + ReplaceHelper.rollback(app_name, HelpersHMR.css_change, sleep=10) + Tns.wait_for_log(log_file=log, string_list=['app.css'], clean_log=False) + + # Verify application looks correct + Tns.wait_for_log(log_file=log, string_list=HelpersHMR.wp_sync, not_existing_string_list=HelpersHMR.wp_errors, + timeout=60) + if platform == Platform.ANDROID: + Helpers.android_screen_match(image=HelpersHMR.image_original, timeout=120) + # if platform == Platform.IOS: + # Helpers.ios_screen_match(sim_id=self.SIMULATOR_ID, image=HelpersHMR.image_original, + # timeout=120) diff --git a/tests/webpack/helpers/helpers.py b/tests/webpack/helpers/helpers.py index 4fbfaf8b..6a82ffea 100644 --- a/tests/webpack/helpers/helpers.py +++ b/tests/webpack/helpers/helpers.py @@ -16,6 +16,22 @@ class Helpers(object): no_wp_run = ['Successfully installed'] no_wp_sync = ['Successfully synced application'] wp = ['Webpack compilation complete'] + wp_run_hmr = ['Webpack compilation complete', 'Successfully installed', 'HMR: Sync', + 'HMR: Hot Module Replacement Enabled. Waiting for signal.'] + wp_sync_hmr = ['Webpack compilation complete', 'Successfully synced application', + 'HMR: Checking for updates to the bundle.'] + wp_errors_hmr = ['Module build failed', 'ENOENT'] + + #HMR + run_hmr = ['Webpack compilation complete', 'Successfully installed', 'HMR: Sync...', + 'HMR: Hot Module Replacement Enabled. Waiting for signal.'] + run_hmr_with_platforms = ['Webpack compilation complete', 'HMR: Sync...', + 'HMR: Hot Module Replacement Enabled. Waiting for signal.', 'Successfully synced application', + 'Refreshing application on device'] + sync_hmr = ['Webpack compilation complete', 'Successfully synced application', + 'HMR: Checking for updates to the bundle.'] + errors_hmr = ['Module build failed', 'ENOENT'] + wp_run = ['Webpack compilation complete', 'Successfully installed'] wp_sync = ['Webpack compilation complete', 'Successfully synced application'] wp_sync_snapshot = ['Webpack compilation complete', 'Successfully synced app', 'Stripping the snapshot flag.']