MSW ב-SvelteKit

כיצד ליישם MSW לפיתוח מקומי ב-SvelteKit

שימוש ב-MSW ב-SvelteKit

זהו מדריך שיראה לך כיצד ליישם את ספריית "Mock Service Worker", המכונה MSW, ביישום SvelteKit שלך. שימו לב שהמדריך הזה לא מראה כיצד להגדיר MSW עם Jest, אלא כיצד להשתמש ב-MSW במהלך פיתוח חי.

היישום המלא זמין במאגר של spikze.club, קישור גם בתוספת בסוף העמוד.

אל תדאג אם המדריך הזה עשוי להיראות מהמם בהתחלה, השינויים די פשוטים. השימוש ב-MSW ב-SvelteKit כדי ללעוג לבקשות במהלך פיתוח מקומי רק דורש קצת יותר מאמץ.

התקנת תלות

ראשית, בואו נתקין את כל התלות הדרושה.

npm i -D msw ts-node concurrently

אנו משתמשים ב-"ts-node" גם כדי להפעיל את ה-server-mocks. לפני שנמשיך, אנא קרא את הפקודה הבאה בשורש הפרויקט של SvelteKit שלך.

npx msw init ./static

זה יפיק קובץ Javascript עם מטפל ברירת המחדל של service worker על ידי MSW. אנו משתמשים ב"סטטי" עבור אפליקציית SvelteKit שלנו, מכיוון שהיא מקבילה לספרייה הציבורית. לבסוף, בואו נעדכן את הקובץ package.json כך ש-MSW ידע היכן לחפש את הקובץ.

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

כסקריפט נוח, הוספתי גם את הפקודה הבאה לתצורת ה"סקריפטים" שלי, הנחוצה אם אתה רוצה להפעיל MSW גם דרך הלקוח וגם השרת (עבור דוגמיות בצד השרת).

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

הכנת מודולי MSW

לפני שנתחיל לכתוב קוד ממשי, עלינו להגדיר את ספריית העבודה החדשה שלנו עבור MSW בתצורה של SvelteKit כמו גם ב-Typescript-config. נתחיל בקובץ Typescript.

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

לאחר מכן, בואו נעדכן את SvelteKit-config.

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")
        }
      }
    }
  }

הוספת הלעגים והמטפלים

בסדר, עברת את החלק הראשון של המדריך! כעת נוסיף את הקוד בפועל שמתבצע בעת שימוש ב-MSW. שימו לב שזו דוגמה מינימלית, לכן הוספתי את מה שצריך כדי שהכל יעבוד, אבל לא יותר.

כפי שאולי ניחשתם מהשינויים שלנו ב-SvelteKit-config, כל הקוד ימוקם בספרייה חדשה בשם "msw", שחי ישירות ב-"src".

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

הנה הקוד להוספת המודולים הבאים. אתה אמור להיות מסוגל פשוט להעתיק ולהדביק אותם, שם הקובץ + נתיב הספרייה כתוב כהערה בכל בלוק בחלק העליון.

//
// 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

אתגר אחד בעת שימוש ב-MSW במהלך פיתוח חי הוא שעלינו לוודא שאין מצב גזע. עלינו להגדיר סדר ביצוע, אחרת ה-Service Worker מ-MSW עשוי להיות פעיל לאחר שכבר בוצעו כל הבקשות.

כדי להשיג מטרה זו, אנו משנים את קובץ פריסת השורש שלנו. מכיוון שהקובץ הזה מותקן עבור כל עמוד, זה מקום טוב לחסום כל ביצוע נוסף עד לסיום MSW.

<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}

מוסיף דף הדגמה

קטעי הקוד הבאים פשוט מציגים תוכן עבור שני דפי הדגמה ונקודת הקצה האחת המשמשת במדריך זה.

<!-- 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"]
  }
});

הוספת משתנה סביבה

כמעט סיימנו. מה שחסר הוא להוסיף את הדגל "VITE_MSW_ENABLED" לסביבה שלנו. אנו משתמשים ב-".env.local" כקובץ שלנו כדי להחזיק את הדגל, מכיוון שהוא ייצרך על ידי Vite ולא יתווסף ל-git.

VITE_MSW_ENABLED=true

הפעלת האפליקציה

בסדר, הכל אמור להיות מוכן לשימוש עכשיו! כדי לאפשר לעג ללקוח, פשוט ודא שהדגל מוגדר. הפעל את הפקודה הנפוצה "dev" ואז כדי ללעוג לבקשות מצד הלקוח.

npm run dev

כדי ללעוג גם לבקשות בצד השרת, פשוט הפעל את הפקודה החדשה שהוספנו בהתחלה.

npm run dev:msw-server

עכשיו אתה מוכן ללעוג לנקודות קצה במהלך הפיתוח המקומי. כפי שצוין בדוגמה של הקוד, דף הדגמה זה אינו כולל דוגמיות בצד השרת, אם כי הכל מוכן לכך. זה אומר שבאופן מקומי, שיחת MSW מופעלת רק אם אתה מנווט מדף האינדקס של אתר ההדגמה לדף ההדגמה בפועל באמצעות לחיצה על הקישור.

כדי לדמות בקשות שרת, יהיו לך נקודות קצה מסובכות יותר שגם מביאות נתונים מבסיסי נתונים. לאחר מכן ניתן ללעוג לבקשות אלו במטפל השרת. שים לב שעבור לעג לשרת, רק כתובות URL מוחלטות תקפות.