MSW a SvelteKitben

Az MSW megvalósítása a helyi fejlesztéshez a SvelteKitben

MSW használata a SvelteKitben

Ez egy oktatóanyag, amely megmutatja, hogyan kell megvalósítani a „Mock Service Worker” könyvtárat, az MSW-t a SvelteKit alkalmazásban. Kérjük, vegye figyelembe, hogy ez az útmutató nem azt mutatja be, hogyan kell beállítani az MSW-t a Jest-tel, hanem azt, hogyan kell használni az MSW-t élő fejlesztés során.

A teljes megvalósítás elérhető a spikze.club tárházában, link is az oldal végén található mellékletben.

Ne aggódjon, ha ez az útmutató elsőre elsöprőnek tűnik, a változtatások meglehetősen egyszerűek. Az MSW használata a SvelteKitben a kérések kigúnyolására a helyi fejlesztés során csak egy kis erőfeszítést igényel.

Függőségek telepítése

Először telepítsük az összes szükséges függőséget.

npm i -D msw ts-node concurrently

A „ts-node”-ot használjuk a szerver-mockok elindításához is. Mielőtt folytatnánk, kérjük, hívja a következő parancsot a SvelteKit-projekt gyökerében.

npx msw init ./static

Ez létrehoz egy Javascript-fájlt az MSW alapértelmezett service worker kezelőjével. A SvelteKit-alkalmazásunkhoz „static”-ot használunk, mivel ez a nyilvános könyvtár megfelelője. Végül frissítsük a package.json-fájlt, hogy az MSW tudja, hol keresse a fájlt.

{
  "scripts": ...
  ...
  "msw": {
    "workerDirectory": "static"
  }
}

Kényelmes szkriptként a következő parancsot is hozzáadtam a „scripts” konfigurációmhoz, amely akkor szükséges, ha az MSW-t kliensen és szerveren keresztül is el akarjuk indítani (szerveroldali mockokhoz).

{
  "scripts": {
    "dev": "svelte-kit dev",
    "dev:msw-server": "concurrently \"cd msw && ts-node server.ts\" \"npm run dev\"",
    ...
  },
}

MSW-modulok előkészítése

Mielőtt elkezdenénk írni a tényleges kódot, meg kell határoznunk az MSW új munkakönyvtárát a SvelteKit konfigurációjában, valamint a Typescript-configban. Kezdjük a Typescript-fájllal.

{
  "compilerOptions": {
    ...,
    "paths": {
      "$lib": ["src/lib"],
      "$lib/*": ["src/lib/*"],
      "$msw": ["src/msw"],
      "$msw/*": ["src/msw/*"]
    }
  }
}

Ezután frissítsük a SvelteKit-config-ot.

import adapter from "@sveltejs/adapter-auto";
import preprocess from "svelte-preprocess";
import path from "path";

/** @type {import('@sveltejs/kit').Config} */
const config = {
  // Consult https://github.com/sveltejs/svelte-preprocess
  // for more information about preprocessors
  preprocess: preprocess(),
  kit: {
    adapter: adapter(),
    vite: {
      ...,
      resolve: {
        alias: {
          $msw: path.resolve("./src/msw"),
          $lib: path.resolve("./src/lib")
        }
      }
    }
  }

A gúnyok és kezelők hozzáadása

Rendben, túljutott az útmutató első részén! Most hozzáadjuk a tényleges kódot, amely az MSW használatakor végrehajtódik. Kérjük, vegye figyelembe, hogy ez egy minimális példa, ezért hozzáadtam azt, ami szükséges ahhoz, hogy minden működjön, de nem többet.

Ahogy azt a SvelteKit-config módosításaiból sejthettük, minden kód egy új „msw” nevű könyvtárba kerül, amely közvetlenül az „src”-ben található.

|- app/
|-- src/
|--- pages/
|--- msw/
|---- fixtures/
...

Itt található a következő hozzáadandó modulok kódja. Egyszerűen másolni-beilleszteni kell őket, a fájlnév + a könyvtár elérési útja minden blokkban megjegyzésként fel van írva felül.

//
// app/src/msw/handlers.server.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

export const handlers = [
  // Here, you can mock absolute URL requests,
  // e.g. to a database. For the current implementation,
  // no data is mocked in this place.
  //
  // Note: This is also the place to mock absolute
  // SSR-imports. Everything in 'handlers.workers.ts'
  // is mocked client-side.
];
//
// app/src/msw/handlers.worker.ts
//

import { rest } from "msw";
import { values } from "./fixtures/msw-demo";

// Mock relative URLs that map to your
// routes' data endpoints. This mock only
// happens for client-side requests.
//
// Note that if you use shadow endpoints, this still works
// as the endpoint gets created by SvelteKit.
export const handlers = [
  rest.get("/msw/demo/__data.json", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json({ values }));
  })
];
//
// app/src/msw/server.ts
//

import { setupServer } from "msw/node";
import { handlers } from "./handlers.server";

export const server = setupServer(...handlers);
//
// app/src/msw/worker.ts 
//

import { setupWorker } from "msw";
import { handlers } from "./handlers.worker";

export const worker = setupWorker(...handlers);
//
// app/src/msw/fixtures/msw-demo.ts
//

export const values = ["foo", "bar"];
//
// app/src/msw/index.ts 
//

import { browser, dev } from "$app/env";

/**
 * Lazy-inject the MSW handler
 * so that no errors happen during
 * build/runtime due to invalid
 * imports from server/client.
 */
export async function inject() {
  if (dev && browser) {
    const { worker } = await import("../msw/worker");
    // For live development, I disabled all warnings
    // for requests that are not mocked. Change how
    // you think it best fits your project.
    return worker.start({ onUnhandledRequest: "bypass" }).catch(console.warn);
  }
  if (dev && !browser) {
    const { server } = await import("../msw/server");
    // Same as in worker-mock above.
    return server.listen({ onUnhandledRequest: "bypass" });
  }
}

MSW indítása

Az egyik kihívás az MSW használata során az élő fejlesztés során, hogy meg kell győződnünk arról, hogy nincs versenyfeltétel. Meg kell határoznunk a végrehajtási sorrendet, különben az MSW szervizmunkása akkor válhat aktívvá, ha már minden kérés megtörtént.

E cél elérése érdekében módosítjuk a gyökérelrendezési fájlunkat. Mivel ez a fájl minden oldalhoz fel van csatolva, jó hely a további végrehajtás blokkolására, amíg az MSW be nem fejeződik.

<script>
  import "../app.css";
  import { dev } from "$app/env";
  
  // Loaded from .env.local, guide covers this
  // step in a moment.
  const isMswEnabled = dev && import.meta.env.VITE_MSW_ENABLED === "true";
  // Flag to defer rendering of components
  // until certain criteria are met on dev,
  // e.g. MSW init.
  let isReady = !isMswEnabled;
  
  if (isMswEnabled) {
    import("$msw")
      .then((res) => res.inject())
      .then(() => (isReady = true));
  }
</script>

{#if isReady}
  <slot />
{/if}

Demo oldal hozzáadása

A következő kódrészletek egyszerűen csak két bemutatóoldal és az ebben az oktatóanyagban használt végpont tartalmát jelenítik meg.

<!-- app/src/routes/msw/index.svelte -->

<script>
  import DisplayProse from "$lib/display/views/DisplayProse.svelte";
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
</script>

<PageLayout>
  <SectionLayout withContentTopSpacing withHeaderSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Landing Page</ProminentDisplayTitle>
    <DisplayProse>
      <p>
        This compoonent has no purpose other than being part of an MSW demo implementation. See <a
          href="https://flaming.codes"
          alt="Link to flaming.codes with blog posts">flaming.codes</a
        > for more details.
      </p>
      <p>
        This page doesn't fetch any data for shows how client-side fetches are mocked with MSW in
        SvelteKit.
      </p>
      <p>Simply click the link below to access the page with data.</p>
      <p>
        <a href="/msw/demo" alt="Link to demo page with data">msw/demo</a>
      </p>
    </DisplayProse>
  </SectionLayout>
</PageLayout>
<!-- app/src/routes/msw/demo.svelte -->

<script lang="ts">
  import ProminentDisplayTitle from "$lib/display/views/ProminentDisplayTitle.svelte";
  import PageLayout from "$lib/layout/views/PageLayout.svelte";
  import SectionLayout from "$lib/layout/views/SectionLayout.svelte";
  export let values: string[];
</script>

<PageLayout>
  <SectionLayout withHeaderSpacing withContentTopSpacing>
    <ProminentDisplayTitle slot="header" color="primary">MSW Demo</ProminentDisplayTitle>
    <p>
      This compoonent has no purpose other than being part of an MSW demo implementation. See <a
        href="https://flaming.codes"
        alt="Link to flaming.codes with blog posts">flaming.codes</a
      > for more details.
    </p>
    <p>
      Values: {values}
    </p>
  </SectionLayout>
</PageLayout>
//
// app/src/routes/msw/demo.ts
//

import type { RequestHandler } from "@sveltejs/kit";

// Just for demo purposes.
export const get: RequestHandler = async () => ({
  status: 200,
  body: {
    values: ["production", "data", "not", "msw"]
  }
});

Környezeti változó hozzáadása

majdnem készen vagyunk. Ami hiányzik, az az, hogy hozzáadjuk a „VITE_MSW_ENABLED” jelzőt a környezetünkhöz. A „.env.local” fájlt használjuk a zászló tárolására, mivel ezt a Vite felhasználja, és nem adja hozzá a git-hez.

VITE_MSW_ENABLED=true

Az alkalmazás futtatása

Rendben, most minden készen áll a használatra! Az ügyfél gúnyolásának engedélyezéséhez egyszerűen győződjön meg arról, hogy a zászló be van állítva. Futtassa a gyakori „dev” parancsot, majd a kliensoldali kérések kijátszásához.

npm run dev

A szerveroldali kérelmek megcsúfolásához egyszerűen futtassa az új parancsot, amelyet az elején hozzáadtunk.

npm run dev:msw-server

Most már készen áll arra, hogy kigúnyolja a végpontokat a helyi fejlesztés során. Amint azt a kódpéldában is megjegyeztük, ez a bemutató oldal nem tartalmaz szerveroldali modelleket, bár minden felkészült rá. Ez azt jelenti, hogy helyileg az MSW-hívás csak akkor indul el, ha a bemutató oldal indexoldaláról a hivatkozásra kattintva a tényleges bemutató oldalra navigál.

A kiszolgálókérések szimulálásához bonyolultabb végpontokkal kell rendelkeznie, amelyek szintén lekérik az adatokat az adatbázisokból. Ezeket a kéréseket ezután meg lehet gúnyolni a szerverkezelőben. Ne feledje, hogy a szerver-mockeknél csak az abszolút URL-ek érvényesek.