diff --git a/resources/js/package.js b/resources/js/package.js index a438eb2ad..d39aa9752 100644 --- a/resources/js/package.js +++ b/resources/js/package.js @@ -15,16 +15,47 @@ import useMask from './stores/useMask' import { swatches, clear as clearSwatches } from './stores/useSwatches' import { clear as clearAttributes } from './stores/useAttributes.js' import './vue' -import { computed } from 'vue' import './fetch' import './filters' import './mixins' -import './turbolinks' +(() => import('./turbolinks'))() import './cookies' import './callbacks' import './vue-components' +if (import.meta.env.VITE_DEBUG === 'true') { + document.addEventListener('vue:loaded', () => { + window.app.$on('notification-message', function (message, type, params, link) { + switch (type) { + case 'error': + console.error(...arguments) + break + case 'warning': + console.warn(...arguments) + break + case 'success': + case 'info': + default: + console.log(...arguments) + } + }) + }) +} + +document.addEventListener('vue:loaded', () => { + const lastStoreCode = useLocalStorage('last_store_code', window.config.store_code) + if (lastStoreCode.value !== window.config.store_code) { + clearAttributes() + clearSwatches() + lastStoreCode.value = window.config.store_code + } +}) + function init() { + if (document.body.contains(window.app.$el)) { + return; + } + // https://vuejs.org/api/application.html#app-config-performance Vue.config.performance = import.meta.env.VITE_PERFORMANCE == 'true' Vue.prototype.window = window @@ -59,101 +90,94 @@ function init() { custom_attributes: [], } - window.app = new Vue({ - el: '#app', - data: { - custom: {}, - config: window.config, - loadingCount: 0, - loading: false, - loadAutocomplete: false, - csrfToken: document.querySelector('[name=csrf-token]').content, - cart: useCart(), - order: useOrder(), - user: useUser(), - mask: useMask(), - showTax: window.config.show_tax, - swatches: swatches, - scrollLock: useScrollLock(document.body), - }, - methods: { - search(value) { - if (value.length) { - Turbo.visit(window.url('/search?q=' + encodeURIComponent(value))) - } - }, - setSearchParams(url) { - window.history.pushState(window.history.state, '', new URL(url)) - }, - toggleScroll(bool = null) { - if (bool === null) { - this.scrollLock = !this.scrollLock - } else { - this.scrollLock = bool - } - }, - - resizedPath(imagePath, size, store = null) { - if (!store) { - store = window.config.store + requestAnimationFrame( + () => { + window.app = new Vue({ + el: '#app', + data: { + custom: {}, + config: window.config, + loadingCount: 0, + loading: false, + loadAutocomplete: false, + csrfToken: document.querySelector('[name=csrf-token]').content, + cart: useCart(), + order: useOrder(), + user: useUser(), + mask: useMask(), + showTax: window.config.show_tax, + swatches: swatches, + scrollLock: useScrollLock(document.body), + }, + methods: { + search(value) { + if (value.length) { + Turbo.visit(window.url('/search?q=' + encodeURIComponent(value))) + } + }, + + setSearchParams(url) { + window.history.pushState(window.history.state, '', new URL(url)) + }, + + toggleScroll(bool = null) { + if (bool === null) { + this.scrollLock = !this.scrollLock + } else { + this.scrollLock = bool + } + }, + + resizedPath(imagePath, size, store = null) { + if (!store) { + store = window.config.store + } + + let url = new URL(imagePath) + url = url.pathname.replace('/media', '') + + return `/storage/${store}/resizes/${size}/magento${url}` + }, + }, + computed: { + // Wrap the local storage in getter and setter functions so you do not have to interact using .value + guestEmail: wrapValue( + useLocalStorage('email', window.debug ? 'wayne@enterprises.com' : '', { serializer: StorageSerializers.string }), + ), + + loggedIn() { + return this.user?.is_logged_in + }, + + hasCart() { + return this.cart?.id && this.cart.items.length + }, + + + canOrder() { + return this.cart.items.every((item) => item.is_available) + }, + }, + watch: { + loadingCount: function (count) { + window.app.$data.loading = count > 0 + }, + }, + mounted() { + setTimeout(() => { + const event = new CustomEvent('vue:mounted', { detail: { vue: window.app } }) + document.dispatchEvent(event) + }) } - - let url = new URL(imagePath) - url = url.pathname.replace('/media', '') - - return `/storage/${store}/resizes/${size}/magento${url}` - }, - }, - computed: { - // Wrap the local storage in getter and setter functions so you do not have to interact using .value - guestEmail: wrapValue( - useLocalStorage('email', window.debug ? 'wayne@enterprises.com' : '', { serializer: StorageSerializers.string }), - ), - - loggedIn() { - return this.user?.is_logged_in - }, - - hasCart() { - return this.cart?.id && this.cart.items.length - }, - - canOrder() { - return this.cart.items.every((item) => item.is_available) - }, - }, - watch: { - loadingCount: function (count) { - window.app.$data.loading = count > 0 - }, - }, - }) - - const lastStoreCode = useLocalStorage('last_store_code', window.config.store_code) - if (lastStoreCode.value !== window.config.store_code) { - clearAttributes() - clearSwatches() - lastStoreCode.value = window.config.store_code - } - - if (window.debug) { - window.app.$on('notification-message', function (message, type, params, link) { - switch (type) { - case 'error': - console.error(...arguments) - break - case 'warning': - console.warn(...arguments) - break - case 'success': - case 'info': - default: - console.log(...arguments) - } - }) - } - - const event = new CustomEvent('vue:loaded', { detail: { vue: window.app } }) - document.dispatchEvent(event) + }) + + setTimeout(() => { + const event = new CustomEvent('vue:loaded', { detail: { vue: window.app } }) + document.dispatchEvent(event) + }) + } + ) } + document.addEventListener('turbo:load', init) +setTimeout(init) diff --git a/resources/views/components/productlist.blade.php b/resources/views/components/productlist.blade.php index 341a264f0..7e64018e6 100644 --- a/resources/views/components/productlist.blade.php +++ b/resources/views/components/productlist.blade.php @@ -24,7 +24,7 @@
-
+
diff --git a/tests/Browser/CartTest.php b/tests/Browser/CartTest.php index 604b38a9d..44b51a5fa 100644 --- a/tests/Browser/CartTest.php +++ b/tests/Browser/CartTest.php @@ -15,6 +15,7 @@ public function addSimpleProduct() $this->browse(function (Browser $browser) { $browser->addProductToCart($this->testProduct->url) ->visit('/cart') + ->waitUntilVueLoaded() ->waitUntilIdle() ->waitFor('@cart-content', 15) ->waitUntilIdle() @@ -43,6 +44,7 @@ public function changeProductQty() { $this->browse(function (Browser $browser) { $browser->visit('/cart') + ->waitUntilVueLoaded() ->waitUntilIdle() ->waitFor('@cart-content', 15) ->waitUntilIdle() @@ -60,6 +62,7 @@ public function removeProduct() { $this->browse(function (Browser $browser) { $browser->visit('/cart') + ->waitUntilVueLoaded() ->waitUntilIdle() ->waitFor('@cart-content', 15) ->waitUntilIdle() diff --git a/tests/Browser/CheckoutTest.php b/tests/Browser/CheckoutTest.php index 4784c3988..ad83f049b 100644 --- a/tests/Browser/CheckoutTest.php +++ b/tests/Browser/CheckoutTest.php @@ -34,6 +34,7 @@ public function checkoutAsUser() // Go through checkout as guest and log in. $this->browse(function (Browser $browser) use ($email) { $browser->waitForReload(fn ($browser) => $browser->visit('/'), 4) + ->waitUntilVueLoaded() ->waitUntilIdle() ->waitFor('@account_menu') ->click('@account_menu') @@ -71,6 +72,7 @@ public function doCheckoutLogin(Browser $browser, $email = false, $password = fa { $browser ->visit('/checkout') + ->waitUntilVueLoaded() ->waitUntilIdle() ->type('@email', $email ?: 'wayne@enterprises.com') ->waitUntilIdle(); diff --git a/tests/Browser/DialogTest.php b/tests/Browser/DialogTest.php index a4e0ab516..53190b94a 100644 --- a/tests/Browser/DialogTest.php +++ b/tests/Browser/DialogTest.php @@ -14,6 +14,7 @@ public function test() { $this->browse(function (Browser $browser) { $browser->visit('/?show-cookie-notice') + ->waitUntilVueLoaded() ->waitUntilIdle() ->assertSee('Accept cookies') ->waitForReload(function (Browser $browser) { diff --git a/tests/Browser/HomepageTest.php b/tests/Browser/HomepageTest.php index 32df98ba8..d9d2b8582 100644 --- a/tests/Browser/HomepageTest.php +++ b/tests/Browser/HomepageTest.php @@ -13,7 +13,7 @@ class HomepageTest extends DuskTestCase public function homepage() { $this->browse(function (Browser $browser) { - $browser->visit('/')->assertSee('All rights reserved.'); + $browser->visit('/')->waitUntilVueLoaded()->assertSee('All rights reserved.'); }); } } diff --git a/tests/Browser/NewsletterTest.php b/tests/Browser/NewsletterTest.php index 54a466b73..0dffd043b 100644 --- a/tests/Browser/NewsletterTest.php +++ b/tests/Browser/NewsletterTest.php @@ -18,6 +18,7 @@ public function test() $this->browse(function (Browser $browser) { $email = $this->faker->email; $browser->visit('/') + ->waitUntilVueLoaded() ->scrollIntoView('@newsletter') ->waitUntilIdle() ->type('@newsletter-email', $email) diff --git a/tests/DuskTestCaseSetup.php b/tests/DuskTestCaseSetup.php index 1556d074e..6ae47bdc8 100644 --- a/tests/DuskTestCaseSetup.php +++ b/tests/DuskTestCaseSetup.php @@ -44,6 +44,16 @@ protected function setUp(): void return $this; }); + Browser::macro('waitUntilVueLoaded', function () { + /** @var Browser $this */ + $this + ->waitUntilIdle() + ->waitUntilTrueForDuration('document.body.contains(window.app?.$el) && window.app?._isMounted && console.log("mounted") === undefined', 10, 1) + ->waitUntilIdle(); + + return $this; + }); + Browser::macro('assertFormValid', function ($selector) { /** @var Browser $this */ $fullSelector = $this->resolver->format($selector); @@ -60,14 +70,17 @@ protected function setUp(): void /** @var Browser $this */ if ($productUrl) { $this - ->visit($productUrl); + ->visit($productUrl) + ->waitUntilVueLoaded() + ->waitUntilIdle(); } // @phpstan-ignore-next-line $this ->waitUntilIdle() - ->pressAndWaitFor('@add-to-cart', 60) - ->waitForText('Added', 60) + ->waitUntilEnabled('@add-to-cart', 200) + ->pressAndWaitFor('@add-to-cart', 120) + ->waitForText(__('Added'), 120) ->waitUntilIdle(); return $this;