1

Within a Svelte app, I have a boolean variable.

import { writable } from 'svelte/store'
export const authorised = writable(false)

This is imported into App.svelte and other svelte files and accessed and updated using $authorised.

I can't use the $ syntax in Cypress so I used

import { authorised } from '../../src/stores'
describe( etc etc
        authorised.set(true)
        cy.visit(`/`)

Within App.svelte, I have a console.log showing value of $authorised.

Using dev tools in the cypress browser output, authorised is showing false.

The test logs in via the backend api, receiving the token and user id in the body response. With this data I set various values in svelte store (including authorised) so that when visit the app, it should show the authorised screen rather than the login screen.

So why is authorised not being set in svelte store?

Updated:

Using Fody's approach, I added to the start of App.svelte, outside of script,

declare global {
  interface Window {
    Cypress?: Cypress.Cypress;
  }
}

Putting this within script gives an error on declare. But the above code, gives an error on interface.

The keyword 'interface' is reserved

I have looked at Parsing error: The keyword 'interface' is reserved and am installing ts-standard and updating the package.json. But the install is taking a long time.

Am I supposed to be installing ts-standard?

Further update:

In main.ts, I added

import type { Writable } from 'svelte/store';    
declare global {
        interface Window {
          Cypress?: Cypress.Cypress
          authorised?: Writable<boolean>
        }
    }

And in App.svelte.

  if (window.Cypress) {
    window.authorised.set($authorised)          
  }

and finally in the test.js file

.its('body').then((body) => {
        cy.log(body)
        cy.visit(`/`)
        cy.window().then(win => win.authorised.set(true))

Running the test shows an error of "windows.authorised is undefined" with a console log of Uncaught TypeError: window.authorised is undefined instance App.svelte:16 init index.mjs:1891 App bundle.js:4033 app main.ts:14 bundle.js:4050

where App.svelte:16 is "window.authorised.set($authorised) "

John
  • 1,593
  • 3
  • 17
  • 28
  • I’m not familiar with cypress. But my educated guess is that, `cy` doesn’t live in the same process as the page does. Thus even though you could import the store and set its value, it’s in fact a diff instance of store. – hackape Jul 20 '22 at 17:01
  • Or even if `cy` is in the same process. Since svelte needs compilation, (I’m not sure about cypress), the store used by svelte, vs the one imported by cypress, might be the same piece of code compiled/bundled independently in 2 diff places. Thus resulting in two independent instances of store. – hackape Jul 20 '22 at 17:07
  • Anyways, I suggest find a way to `===` compare the two, like attach them to global object so they can meet each other. – hackape Jul 20 '22 at 17:10
  • `window.authorised.set($authorised)` is incorrect. You ***must*** initialize `window.authorised` before calling it - that's what the error is telling you. In fact, it's a waste of time adding `authorised` to window if you don't use it in the test. – SuchAnIgnorantThingToDo-UKR Jul 22 '22 at 10:16

2 Answers2

1

If you import the writable store directly, it's not using the same instance as the one in the app.

But you can attach it as a property of window to allow Cypress access to the same instance.

A minimal example:

Svelte app

<script>
  import { writable } from 'svelte/store'

  export const authorised = writable(false)

  if (window.Cypress) {
    window.authorised = authorised;          // only if Cypress is running
  }

  let authorisedValue;
  authorised.subscribe(value => {
    authorisedValue = value;
  });
</script>

<main>
  <div id="app-container" class="app-container">
    <div id="authorised-display-value">{authorisedValue}</div>
  </div>
</main>

Test

it('passes', () => {
  cy.visit('http://localhost:8080/')
  cy.get('#authorised-display-value').should('contain', 'false')    // passes
  cy.window().then(win => win.authorised.set(true))
  cy.get('#authorised-display-value').should('contain', 'true')     // passes
})

Typescript support

If the project is typescript-based, the window.Cypress and window.authorised references will give a problem.

Define these in main.ts.

For my minimal app:

import type { Writable } from 'svelte/store';
import App from './App.svelte';

declare global {
  interface Window {
    Cypress?: Cypress.Cypress;
    authorised?: Writable<Boolean>;
  }
}

const app = new App({
    target: document.body
});

export default app;

It's also good practice to add a generic parameter to the writable store.

In App.svelte:

<script lang="ts">
  import { writable } from 'svelte/store'
  export const authorised = writable<Boolean>(false)
  ...

Finally, define the global type imports in global.d.ts:

/// <reference types="svelte" />
/// <reference types="cypress" />
Fody
  • 23,754
  • 3
  • 20
  • 37
  • installed ts-standard, updated package.json but still getting keyword "interface" is reserved error. I do realise that using the UI to login for cypress is perfectly adequate but would like this to work because it is now interesting. So obviously I am doing something wrong compared to the example code above. Also, just to check in the cypress test code, I do not need to import authorised from stores.ts? – John Jul 21 '22 at 09:25
  • I'm not sure you can add any javascript outside of the ` – Fody Jul 21 '22 at 09:34
  • And no the `import { authorised } from '../../src/stores'` is not needed in the Cypress test. – Fody Jul 21 '22 at 09:39
  • Updated for Typescript. – Fody Jul 21 '22 at 11:08
  • So you defined an object type window. Shouldn't there be an instance of that interface? Also re authorised - is authorised defined in store that is used throughout the svelte code and there is the window.authorised that can be updated by cypress. So should the store'd authorised not be set to the value of the window authorised rather than the reverse? If cypress is not running then the handleLogin code will set store authorised. If cypress is running then it will set stor authorised? Also, with non typescript code, subscribe in App.svelte - why not used the $ syntax? – John Jul 22 '22 at 11:29
0

What I got working was a mixture of @fody's answer and mine. So in main.ts - I added the additions to the Window interface so that cypress and the app can communicate via this. In App.svelte - I added (the variables preceeded by $ are svelte store variables)

 if (window.Cypress) {
    console.log("entering Cypress selection")
    authorised = window.authorised
    $emailName = window.emailName
    $authToken = window.authToken
    $userId = window.userId
  }

And in the test

  cy.request('POST', Cypress.env('api')+'users/login', {
    email: "johnny@email.com",
    password: "12345678"
  }).its('body').then((body) => {
            cy.log(body)

            cy.visit(`/`, {
              onBeforeLoad(win) {
                win.authorised = true
                win.emailName = "johnny@email.com"
                win.authToken = body.token
                win.userId = body.userId
              },
            })
                                    
          cy.get('h1').should('contain', 'Svelte To-Do List')       

So the key bit is the onBeforeLoad as that updates the new app window with the auth information. Without this, there are two different window objects and information is not passed across.

John
  • 1,593
  • 3
  • 17
  • 28