We are a team of young professionals striving to deliver best valued products and services to our customers.

 

follow us

 > Web Development  > How to Time Travel in Laravel Dusk using Carbon

How to Time Travel in Laravel Dusk using Carbon

Time travelling is an important aspect of testing for time-sensitive operations/data of your Laravel application.

What if you are doing a Black Box Browser test with Laravel Dusk and requires to time travel (mocking time)? Well you could’ve just use Carbon::setTestNow($date);. That’s not the the case though, you will notice browser recorded time is different that in your test case(you can use Laravel Log to log the time when running test).

Why is browser time and test process is not in synced ? Well it’s because …

Dusk test and the Browser application runs as separate processes.

/** Start Laravel Dusk Test
*   Assume today's date is 2020-03-01
*/
public function testGetMyBooks() 
{
    Carbon::setTestNow(Carbon::parse('2020-01-01'));
    Carbon::now(); // 2020-01-01
    $this->browse(function (Browser $browser) {
        $browser->visit('/login')
        /* On browser test side Carbon::now() -> 2020-03-01 */
    });
}

I’ve struggled for finding ways to time travel in Laravel Dusk since there’s not much devs are facing the scenario as mine. Here’s a neat trick for your to archive time travelling with Laravel Dusk without breaking a sweat.

Step 1
We are going start from browser test setup. Before the browser start any action, we need to execute a script to store the date that you going to pass by in your browser local storage.

public function testGetMyBooks() 
{
    $this->browse(function (Browser $browser) {
        $browser->script("window.localStorage.setItem('carbon-date', '2020-01-01');");
        $browser->visit('/login')
        /* On browser test side Carbon::now() -> 2020-03-01 */
    });
}

Step 2
Ensure to set your Laravel app environment APP_ENV to testing in .env.dusk.local.

Then, add this line <script> window.env = '{{ config('app.env') }}'; </script> to app.blade.php or your main entry blade file head section to configure your app env that can be read from client-side.

Step 3
Assuming you are using Axios for sending network requests to PHP, we shall make use of axios interceptors to append a header.
Add code below in your app.js or main.js file to append the desired date to request header so that every axios request will pass in the date value from local storage to PHP.

axios.interceptors.request.use(config => {    
    if (window.env === 'testing' && window.localStorage.getItem('carbon-date')) {
        config.headers['Carbon-Date'] = window.localStorage.getItem('carbon-date');
    }
    return config;
}, error => {
    return Promise.reject(error);
});

Step 4
Create a new middleware file named CarbonSetTestDate and register this class in your kernel middleware. After that, add the code below in your middleware handle() method. So every request will go through the CarbonSetTestDate middleware for time travel requests.

public function handle($request, Closure $next)
{
        if(config('app.env') == 'testing' && $request->header('Carbon-Date')) {
            Carbon::setTestNow(Carbon::parse($request->header('Carbon-Date')));
        }
        return $next($request);
}

Summary
The reason why we make every request to change date via the middleware before function execution is due to each request has it own session and process. To protect your system, ensure both side frontend and backend validates app environment to avoid malicious attempts on changing the date time of your running application.