diff --git a/.gitignore b/.gitignore index 0d20b64..d0a113f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.pyc +.DS_Store \ No newline at end of file diff --git a/readme.md b/readme.md index 7cbce03..eb29b13 100644 --- a/readme.md +++ b/readme.md @@ -23,6 +23,10 @@ run_phpunit_tests_in_dir run_single_phpunit_test run_last_phpunit_test run_all_phpunit_tests +run_single_dusk_test +run_all_dusk_tests +run_dusk_tests_in_dir +run_pint_on_current_file ```` Here are some example keybindings: @@ -59,3 +63,35 @@ If you use [fish shell](https://fishshell.com/), specify this in your settings: ``` This will instruct Sublime PHPUnit to connect the commands using fish's `; and` instead of bash's `&&`. + +## Docker Support + +If your PHP project runs inside Docker, you can have all commands execute inside a container via `docker compose exec`. Add the `phpunit-sublime-docker-service` setting to your `.sublime-project` file: + +```json +{ + "settings": { + "phpunit-sublime-docker-service": "app" + } +} +``` + +Where `app` is the name of your Docker Compose service. When this setting is present, commands will be wrapped like: + +``` +cd /path/to/project && docker compose exec -e XDEBUG_MODE=off app php vendor/bin/phpunit tests/Feature/SomeTest.php +``` + +The `cd` runs on the host so `docker compose` can find your `docker-compose.yml`. File paths are automatically converted to project-relative paths for the container. + +When this setting is absent, all commands run locally as usual. + +## Auto-focus Sublime Text After Running Tests + +If you want Sublime Text to automatically regain focus after a test is triggered, enable the following setting: + +```json +{ + "phpunit-sublime-autofocus-after-run": true +} +``` diff --git a/sublime-phpunit.py b/sublime-phpunit.py index 259d76f..485137e 100644 --- a/sublime-phpunit.py +++ b/sublime-phpunit.py @@ -84,19 +84,51 @@ def run_in_terminal(self, command): self.lastTestCommand = command os.system(osascript_command) + # Auto-focus Sublime Text if setting is enabled + if self.get_setting('phpunit-sublime-autofocus-after-run', False): + self.focus_sublime_text() + + def focus_sublime_text(self): + focus_command = 'osascript -e "tell application \\"System Events\\" to keystroke tab using {command down}"' + os.system(focus_command) + + def get_docker_service(self): + view = self.window.active_view() + if view: + return view.settings().get('phpunit-sublime-docker-service') + return None + + def relative_path(self, path, base): + clean_path = path.replace('\\ ', ' ') + clean_base = base.replace('\\ ', ' ') + rel = os.path.relpath(clean_path, clean_base) + return rel.replace(' ', '\\ ') + + def docker_cmd(self, service): + return 'docker compose exec -e XDEBUG_MODE=off ' + service + class RunPhpunitTestCommand(PhpunitTestCommand): def run(self, *args, **kwargs): file_name, phpunit_config_path, phpunit_bin, active_view, directory = self.get_paths() - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + file_name) + docker_service = self.get_docker_service() + if docker_service: + rel_file = self.relative_path(file_name, phpunit_config_path) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php vendor/bin/phpunit ' + rel_file) + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + file_name) class RunAllPhpunitTestsCommand(PhpunitTestCommand): def run(self, *args, **kwargs): file_name, phpunit_config_path, phpunit_bin, active_view, directory = self.get_paths() - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin) + docker_service = self.get_docker_service() + if docker_service: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php vendor/bin/phpunit') + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin) class RunSinglePhpunitTestCommand(PhpunitTestCommand): @@ -106,7 +138,13 @@ def run(self, *args, **kwargs): current_function = self.get_current_function(active_view) - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + file_name + " --filter '/::" + current_function + "$/'") + docker_service = self.get_docker_service() + if docker_service: + rel_file = self.relative_path(file_name, phpunit_config_path) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php vendor/bin/phpunit ' + rel_file + " --filter '/::" + current_function + "$/'") + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + file_name + " --filter '/::" + current_function + "$/'") + class RunLastPhpunitTestCommand(PhpunitTestCommand): @@ -123,7 +161,12 @@ class RunPhpunitTestsInDirCommand(PhpunitTestCommand): def run(self, *args, **kwargs): file_name, phpunit_config_path, phpunit_bin, active_view, directory = self.get_paths() - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + directory) + docker_service = self.get_docker_service() + if docker_service: + rel_dir = self.relative_path(directory, phpunit_config_path.replace('\\ ', ' ')) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php vendor/bin/phpunit ' + rel_dir) + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + phpunit_bin + ' ' + directory) class RunSingleDuskTestCommand(PhpunitTestCommand): @@ -132,21 +175,58 @@ def run(self, *args, **kwargs): current_function = self.get_current_function(active_view) - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk ' + file_name + ' --filter ' + current_function) + docker_service = self.get_docker_service() + if docker_service: + rel_file = self.relative_path(file_name, phpunit_config_path) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php artisan dusk ' + rel_file + ' --filter ' + current_function) + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk ' + file_name + ' --filter ' + current_function) class RunAllDuskTestsCommand(PhpunitTestCommand): def run(self, *args, **kwargs): file_name, phpunit_config_path, active_view, directory = self.get_paths() - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk') + docker_service = self.get_docker_service() + if docker_service: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php artisan dusk') + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk') class RunDuskTestsInDirCommand(PhpunitTestCommand): def run(self, *args, **kwargs): file_name, phpunit_config_path, active_view, directory = self.get_paths() - self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk ' + directory) + docker_service = self.get_docker_service() + if docker_service: + rel_dir = self.relative_path(directory, phpunit_config_path.replace('\\ ', ' ')) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php artisan dusk ' + rel_dir) + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + 'php artisan dusk ' + directory) + + +class RunPintOnCurrentFileCommand(PhpunitTestCommand): + + def find_pint_bin(self, directory): + binpath = os.path.realpath(directory + "/vendor/bin/pint") + + if os.path.isfile(binpath.replace("\\", "")): + return binpath + else: + return 'vendor/bin/pint' + + def run(self, *args, **kwargs): + file_name, phpunit_config_path, phpunit_bin, active_view, directory = self.get_paths() + + pint_bin = self.find_pint_bin(phpunit_config_path) + + docker_service = self.get_docker_service() + if docker_service: + rel_file = self.relative_path(file_name, phpunit_config_path) + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + self.docker_cmd(docker_service) + ' php vendor/bin/pint ' + rel_file) + else: + self.run_in_terminal('cd ' + phpunit_config_path + self.get_cmd_connector() + pint_bin + ' ' + file_name) class FindMatchingTestCommand(sublime_plugin.WindowCommand): diff --git a/sublime-phpunit.sublime-commands b/sublime-phpunit.sublime-commands index 6a8f16d..b5190c6 100644 --- a/sublime-phpunit.sublime-commands +++ b/sublime-phpunit.sublime-commands @@ -7,5 +7,6 @@ { "caption": "Sublime PHPUnit: Run Dusk In This Directory", "command": "run_dusk_tests_in_dir" }, { "caption": "Sublime PHPUnit: Run Dusk On This File", "command": "run_dusk_test" }, { "caption": "Sublime PHPUnit: Run Dusk On This Method", "command": "run_single_dusk_test" }, - { "caption": "Sublime PHPUnit: Run All Dusk Tests", "command": "run_all_dusk_tests" } + { "caption": "Sublime PHPUnit: Run All Dusk Tests", "command": "run_all_dusk_tests" }, + { "caption": "Laravel Pint: Fix Current File", "command": "run_pint_on_current_file" } ]