Back to "अंतिम- से- अंत जाँच पुंजर के साथ - एक उचित विकल्प सेलेनियम के लिए"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

E2E Testing PuppeteerSharp Testing xUnit

अंतिम- से- अंत जाँच पुंजर के साथ - एक उचित विकल्प सेलेनियम के लिए

Thursday, 27 November 2025

आधुनिक E2-TE (अंत-EEEE- अंत का उपयोग करके, उपयोक्ताओं की तरह अपनी साइटों का उपयोग करने के लिए नहीं है). यह विस्तृत गाइड आपको दिखाता है कि कैसे तेज, विश्वस्त ब्राउज़र एजेंट के लिए तीव्र परीक्षा के लिए उपयोग करने के लिए। TETTCTCTCT-S और वेब परीक्षण के लिए मूल परीक्षणों से सब कुछ गुप्त परीक्षण। Dhribi-SIN-S, मैं इस नए परीक्षण के लिए चुना गया है, क्योंकि अगर मैं जानता था कि यह काफी आसान है, और मेरे लिए मेरी मदद की आवश्यकता थी। सीधे मार्गदर्शक खेलें बजाय।

परिचय

आप कभी के साथ काम किया है अगर सेलेनियम अंत के लिए जाँच के लिए, आप यह पता चल जाएगा कि यह पीठ में एक सही दर्द हो सकता है. ड्राइवर संस्करण के साथ संघर्ष, अपने मशीन पर काम करता है कि हवापूर्ण परीक्षण के साथ, और कहीं और, और नहीं, और वेब हार्डवेयर प्रोटोकॉल प्रोटोकॉल के सामान्य आलसीपन, यह आप पर्याप्त बनाने के लिए आप यह सब और अधिक करना चाहते हैं.

प्रविष्ट करें स्केल्स: (S) - गूगल के लिए .नेट पोर्ट स्केल्स: (S) पुस्तकालय. यह सेलेनियम छोटे, तेजी से चचेरे भाई की तरह है जो वास्तव में समय पर दिखाने के लिए परेशान है और आप 17 मिनट अलग ब्राउज़र ड्राइवरों को डाउनलोड करने की जरूरत नहीं है.

इस लेख में, मैं आप के माध्यम से चल रहा हूँ कि कैसे मैं इस बहुत ही ब्लॉग पर ई2ई परीक्षण के लिए मजबूत परीक्षण, पुनःपो से वास्तविक कोड उदाहरणों के साथ पूरा किया है. हम परीक्षण, PDF पीढ़ी, वेब खोजें, और इसके विकल्पों के साथ तुलना करेंगे.

तो puepputer क्या है?

स्केल्स: (S) एक .ENT लाइब्रेरी है एक उच्च- स्तर API प्रदान करता है क्रोमाला या क्रोमानियम ब्राउज़र को नियंत्रित करने के लिए क्रोमाडेव औजार प्रोटोकॉलComment. से विपरीत सेलेनियम, जो उपयोग करता है वेबDriver प्रोटोकॉल (एक बजाय नीफ HTTP आधारित फेसबुक तार प्रोटोकॉल), Taver CPAS औजार के माध्यम से सीधे ब्राउज़र पर भाषण देता है.

ज़रा सोचिए:

  • सेलेनियम: अपने ब्राउज़र से संपर्क करने के लिए पत्र भेजने की तरह
  • स्केल्स: (S): ब्राउज़र के मस्तिष्क के लिए एक सीधा फोन लाइन होने की तरह
graph LR
    A[Test Code] -->|WebDriver Protocol| B[Selenium]
    B -->|JSON Wire Protocol| C[Browser Driver]
    C -->|Commands| D[Browser]

    E[Test Code] -->|DevTools Protocol| F[PuppeteerSharp]
    F -->|Direct Connection| G[Chrome/Chromium]

    style A stroke:#333,stroke-width:2px
    style E stroke:#333,stroke-width:2px
    style F stroke:#0066cc,stroke-width:3px
    style G stroke:#0066cc,stroke-width:3px

आपकी जाँच - पड़ताल में ए2 परीक्षा कहाँ है

इससे पहले कि हम गहरा बल देते हैं, चलो इस बारे में बात करते हैं कि E2EEE परीक्षा चीजों की महान योजना में फिट होती है. आप शायद परीक्षण के बारे में सुना है कि यह वास्तव में अभ्यास में कैसे काम करता है:

graph TB
    subgraph "Testing Pyramid"
        E2E[E2E Tests<br/>Few, Slow, High Confidence<br/>Test full user journeys]
        INT[Integration Tests<br/>Medium number, Medium speed<br/>Test component interactions]
        UNIT[Unit Tests<br/>Many, Fast, Low Cost<br/>Test individual functions]
    end

    subgraph "Trade-offs"
        SPEED[Speed]
        CONF[Confidence]
        COST[Cost]
    end

    subgraph "When to Use E2E"
        W1[Critical user journeys<br/>e.g. checkout, login]
        W2[Cross-browser compatibility]
        W3[JavaScript-heavy UIs]
        W4[Complex user interactions]
    end

    E2E -.->|Slow but high confidence| CONF
    INT -.->|Balanced| SPEED
    UNIT -.->|Fast and cheap| SPEED

    E2E -.->|Expensive to run| COST
    UNIT -.->|Cheap to run| COST

    style E2E stroke:#cc0000,stroke-width:3px
    style INT stroke:#ff9900,stroke-width:2px
    style UNIT stroke:#00aa00,stroke-width:2px
    style CONF stroke:#0066cc,stroke-width:2px
    style SPEED stroke:#00aa00,stroke-width:2px
    style COST stroke:#cc0000,stroke-width:2px

सच मुच होने वाली (क़यामत)

  • इकाई जाँच (80% आपके परीक्षणों का): फास्ट, महँगा, निजी कार्य. लेकिन वे आपको नहीं बताते कि क्या वास्तव में तंत्र पूरी तरह काम करता है.
  • एकीकरण परीक्षण (अपनी जाँचों में से 15%). जाँच करें कि कैसे अलग अलग भागों को एक साथ काम करते हैं. E2E से अधिक तेज लेकिन पूर्ण यूआई परीक्षण नहीं करते.
  • एस2 परीक्षण (अपनी जाँचों में5%) धीमी, महँगी, परंतु सिस्टम को ठीक जैसे उपयोक्ता इसे अनुभव करते हैं, जाँच करें. यह वह जगह है जहाँ Tucute तेज चमकती है.

जब आपको 12 परीक्षा की ज़रूरत होती है:

  1. गंभीर उपयोक्ता सफ़र - लॉगइन, चेक आउट, भुगतान प्रक्रिया. यदि ये ब्रेक, आपका व्यापार बंद हो जाता है.
  2. जावास्क्रिप्ट- SECP यूआई - आधुनिक प्रगति के रूप में (के रूप में)फिर से कोशिश करें, वी-, कोणीय) जहां यूआई को ग्राहक के बगल में अनुवादित किया गया है.
  3. क्रॉस- ब्राउज़र समस्याएँ - अलग ब्राउज़र चीजों को अलग तरीके से रेंडर करता है (हालाँकि आप सिर्फ क्रोमोसो - केवल कर रहे हैं).
  4. जटिल व्यवहार - मल्टी- स्टेप विजार्ड, खींचें तथा ड्रॉप, फाइल अपलोड करें.

जब आपको ई2 परीक्षण की आवश्यकता नहीं है:

  1. सादा दोष की क्रिया - संयोजन परीक्षण काफी हैं.
  2. शुद्ध तर्क - यह इकाई जाँच के लिए क्या कर रहे हैं.
  3. प्रत्येक किनारा केस - इ2E जाँच बहुत धीमा और प्रभावी परीक्षण के लिए महँगी हैं.

सेलेनियम से ज़्यादा पुख्ता क्यों?

मुझे रास्ता गिनने दो:

  1. कोई ड्राइवर प्रबंधन Faff नहीं: sppppeopoperer तीव्र डाउनलोड और आप के लिए क्रोमोस ब्राउज़र प्रबंधित करता है. कोई भी अधिक multolsovioIssssols संस्करण के साथ नहीं है जो आपके संस्थापित क्रोमो संस्करण से मेल नहीं खाते.

  2. तेज चलाना: डीएव औजार प्रोटोकॉल बहुत तेजी से वेब डी डीएससी की तुलना में अधिक तेजी से है. आपकी जाँच जल्दी चला जाएगा, और आप चीजों के होने की प्रतीक्षा करने के लिए कम समय खर्च करेंगे.

  3. बेहतर एपीआई: एपीआई और अधिक आधुनिक है। यह अतुल्यकालिक/एक पूरी तरह से नीचे है, जो आधुनिक विकास के बारे में बहुत अच्छी तरह से सही है।

  4. अंतर्निर्मित स्क्रीनशॉट व पीडीएफ तैयार करना: जब एक परीक्षण असफल हो जाए तो स्क्रीनशॉट लेना चाहते हो? यह पुपिपर के साथ सरल है.

  5. इंटरफेस नेटवर्क निवेदन: आप इंटरनेट पर रोक लगा सकते हैं या गलत काम कर सकते हैं ।

  6. उचित जावास्क्रिप्ट चलाना: जावास्क्रिप्ट पृष्ठ संदर्भ में चलाएँ और ऐसे परिणाम प्राप्त करें जिससे आप रो नहीं सकते.

मुख पृष्ठ को सुस्पष्ट करने के लिए सेटअप किया जा रहा है

प्रथम, जोड़िए स्केल्स: (S) नु पाने का पैकेज:

dotnet add package PuppeteerSharp

यहाँ मेरी जाँच परियोजना कॉन्फ़िगरेशन है (Mostlylucid.Test/Mostlylucid.Test.csproj:23):

<PackageReference Include="PuppeteerSharp" Version="20.2.4" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

मैं उपयोग कर रहा हूँ एक्स- इकाई (SACP.Nont का डिफ़ॉल्ट, लेकिन pucuter समान रूप से काम करता है के साथ एन- इकाई या एमएस टेस्ट.

बेस टेस्ट क्लास बनाया जा रहा है

प्रत्येक परीक्षा में सेटअप/ट्रॉज कोड को दोहराने के बजाय, मैं एक आधार वर्ग बनाया है (e)Mostlylucid.Test/E2E/E2ETestBase.cs:12) जो ब्राउज़र जीवन - संबंधी प्रबंधन संभालता है:

क्लास स्ट्रक्चर

using PuppeteerSharp;
using Xunit.Abstractions;

namespace Mostlylucid.Test.E2E;

public abstract class E2ETestBase : IAsyncLifetime
{
    protected readonly ITestOutputHelper Output;
    protected IBrowser Browser = null!;
    protected IPage Page = null!;

    protected const string BaseUrl = "http://localhost:8080";
    protected const int DefaultTimeout = 30000;

    protected E2ETestBase(ITestOutputHelper output)
    {
        Output = output;
    }

हम लागू करें आई. वी. एक्स- इकाई से, जो अतुल्यकालिक सेटअप /ट प्रदान करता है. पारंपरिक निर्माणकर्ता, यह ठीक से ब्राउज़र प्रारंभ करने की प्रतीक्षा करता है.

ब्राउज़र प्रारंभ किया जा रहा है

    public async Task InitializeAsync()
    {
        // Download Chromium on first run
        var browserFetcher = new BrowserFetcher();
        await browserFetcher.DownloadAsync();

        // Launch browser with sensible defaults
        Browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true, // Set false for debugging
            DefaultViewport = new ViewPortOptions
            {
                Width = 1400,
                Height = 900
            },
            Args = new[]
            {
                "--no-sandbox",
                "--disable-setuid-sandbox"
            }
        });

        Page = await Browser.NewPageAsync();
        Page.DefaultTimeout = DefaultTimeout;
    }

वह BrowserFetcher पहले रन - कोई हस्तचालित ड्राइवर प्रबंधक की आवश्यकता नहीं है. --no-sandbox डॉकer/CIआई वातावरण के लिए फ्लैग जरूरी हैं.

साफ

    public async Task DisposeAsync()
    {
        if (Page != null) await Page.CloseAsync();
        if (Browser != null) await Browser.CloseAsync();
    }
}

सही उपयोग स्मृति रिसाव से बचने के लिए महत्वपूर्ण है. हर ब्राउज़र उदाहरण रैम के 100-200MB का उपयोग करता है.

सहायक विधि

बेस क्लास में यह भी शामिल है कि येरलेट कम करने के लिए सहायक तरीके हों (एम्प्शन कम करें)Mostlylucid.Test/E2E/E2ETestBase.cs:72-172):

// Navigation with automatic network idle waiting
protected async Task NavigateAsync(string path)
{
    var url = path.StartsWith("http") ? path : $"{BaseUrl}{path}";
    await Page.GoToAsync(url, new NavigationOptions
    {
        WaitUntil = new[] { WaitUntilNavigation.Networkidle2 }
    });
}

// Safe element waiting with timeout handling
protected async Task<IElementHandle?> WaitForSelectorAsync(string selector, int timeout = 5000)
{
    try
    {
        return await Page.WaitForSelectorAsync(selector, new WaitForSelectorOptions
        {
            Timeout = timeout,
            Visible = true
        });
    }
    catch (WaitTaskTimeoutException)
    {
        return null; // Graceful degradation
    }
}

// Common element operations
protected async Task<bool> ElementExistsAsync(string selector) =>
    await Page.QuerySelectorAsync(selector) != null;

protected async Task<string?> GetTextContentAsync(string selector)
{
    var element = await Page.QuerySelectorAsync(selector);
    return element == null ? null :
        await Page.EvaluateFunctionAsync<string>("el => el.textContent", element);
}

protected async Task TypeAsync(string selector, string text, int delay = 50)
{
    await Page.WaitForSelectorAsync(selector);
    await Page.TypeAsync(selector, text, new TypeOptions { Delay = delay });
}

protected async Task ClickAsync(string selector)
{
    await Page.WaitForSelectorAsync(selector);
    await Page.ClickAsync(selector);
}

जब सीआई में जाँच असफल हो जाती है, तो इन तत्वों के अस्तित्त्व की प्रतीक्षा - प्रतीक्षा करते हैं, सुप्रसिद्ध टाइम हैंडल करते हैं, और स्वचालित लॉगिंग के लिए स्वचालित लॉगिंग करते हैं ।

वास्तविक जाँच लिख रहा है

ठीक है, चलो अच्छी सामग्री को प्राप्त करते हैं वास्तविक परीक्षण. यहाँ मेरे ब्लॉग की फ़िल्टर पट्टी से एक वास्तविक जाँच है (या ठीक है)Mostlylucid.Test/E2E/FilterBarTests.cs:20-50):

[Fact(Skip = "Local E2E test - requires site to be running on localhost:8080")]
public async Task FilterBar_LanguageDropdown_ShowsLanguages()
{
    // Arrange
    await NavigateAsync("/blog");

    // Act - Click the language dropdown button
    var dropdownButton = await WaitForSelectorAsync("#LanguageDropDown button");
    Assert.NotNull(dropdownButton);

    await ClickAsync("#LanguageDropDown button");
    await WaitAsync(300);

    // Assert - Dropdown menu should be visible with language options
    var dropdownOpen = await EvaluateFunctionAsync<bool>(@"() => {
        const dropdown = document.querySelector('#LanguageDropDown div[x-show]');
        if (!dropdown) return false;
        const style = window.getComputedStyle(dropdown);
        return style.display !== 'none';
    }");

    Assert.True(dropdownOpen, "Language dropdown should be open");

    // Check that English option exists
    var hasEnglish = await EvaluateFunctionAsync<bool>(@"() => {
        const options = document.querySelectorAll('#LanguageDropDown li a');
        return Array.from(options).some(opt => opt.textContent.toLowerCase().includes('english'));
    }");

    Assert.True(hasEnglish, "Language dropdown should contain English option");
    Output.WriteLine("✅ Language dropdown shows languages correctly");
}

यह जाँच की जा रही है कि मेरी भाषा नीचे काम करती है. चलो देखते हैं कि यह किस बात की जाँच करता है.

छोड़ें गुण

[Fact(Skip = "Local E2E test - requires site to be running on localhost:8080")]

मैंने इस जाँच को डिफ़ॉल्ट से छोड़ दिया है क्योंकि यह स्थानीय रूप से चल रहा है. E2 जाँच के लिए, आप आमतौर पर उन्हें चलाने के लिए चाहते हैं हर बिल्ड के बजाय उन्हें चलाने के लिए। आप उन्हें नहीं खोल सकते जब आप उन्हें चलाने के लिए तैयार कर रहे हैं, या एक अलग सी मै में चलाने के लिए जहाँ आप उस जगह का उपयोग किया गया है।

जावास्क्रिप्ट चलाया जा रहा है

var dropdownOpen = await EvaluateFunctionAsync<bool>(@"() => {
    const dropdown = document.querySelector('#LanguageDropDown div[x-show]');
    if (!dropdown) return false;
    const style = window.getComputedStyle(dropdown);
    return style.display !== 'none';
}");

यह ऐसी जगहों में से एक है जहाँ पुख्ता करनेवाला पूर्ण रूप से चमकता है । EvaluateFunctionAsync विधि आप ब्राउज़र संदर्भ में जावास्क्रिप्ट चलाने देता है और परिणाम को उचित रूप से प्राप्त करता है.

इसे सेलेनियम की तुलना करें जहाँ आप की आवश्यकता होगी:

  1. जोडीयॉं ढुॅंढे
  2. इसका प्रदर्शन गुण प्राप्त करें
  3. स्ट्रिंग परिणाम पार्से करें
  4. आशा है कि आप इसे जाँच समय द्वारा नसीहत नहीं है

HMSX इंटरेक्शन जाँच की जा रही है

मेरा ब्लॉग इस्तेमाल करता है एचएमएमएक्स बहुत से (सर्वर को बिना लिखा जावास्क्रिप्ट के). यहाँ एक जांच है जो कि कार्यशीलता को जांचता है (संशिक रूप से लागू करता है)Mostlylucid.Test/E2E/FilterBarTests.cs:98-126):

[Fact(Skip = "Local E2E test - requires site to be running on localhost:8080")]
public async Task FilterBar_SortOrder_ChangesPostOrder()
{
    // Arrange
    await NavigateAsync("/blog");

    // Get the first post title before sorting
    var firstPostBefore = await EvaluateFunctionAsync<string>(@"() => {
        const postLink = document.querySelector('.post-title, article h2 a, #contentcontainer article a');
        return postLink?.textContent?.trim() || '';
    }");
    Output.WriteLine($"First post before sort: {firstPostBefore}");

    // Act - Change sort order to "Oldest first"
    await Page.SelectAsync("#orderSelect", "date_asc");
    await WaitAsync(1000); // Wait for HTMX to update

    // Assert - Post order should have changed
    var firstPostAfter = await EvaluateFunctionAsync<string>(@"() => {
        const postLink = document.querySelector('.post-title, article h2 a, #contentcontainer article a');
        return postLink?.textContent?.trim() || '';
    }");
    Output.WriteLine($"First post after sort: {firstPostAfter}");

    var selectValue = await EvaluateFunctionAsync<string>("() => document.querySelector('#orderSelect')?.value");
    Assert.Equal("date_asc", selectValue);
    Output.WriteLine("✅ Sort order selection works correctly");
}

यहाँ कुंजी है await WaitAsync(1000) चुने गए मूल्य को बदलने के बाद HMMX एक पल की जरूरत है यह अनुरोध और डोम अद्यतन करने के लिए। एक परिपूर्ण दुनिया में, हम पूरा करने के लिए एक विशिष्ट नेटवर्क अनुरोध के लिए इंतजार करेंगे, लेकिन सरल मामलों के लिए, एक छोटा सा देरी ठीक है।

डिज़ाइन जाँच रहा है

यहाँ एक गालिक जाँच है कि मेरा फिल्टर पट्टी ठीक से मोबाइल उपकरणों पर छुपा हुआ है (e)Mostlylucid.Test/E2E/FilterBarTests.cs:216-245):

[Fact(Skip = "Local E2E test - requires site to be running on localhost:8080")]
public async Task FilterBar_ResponsiveDesign_HiddenOnMobile()
{
    // Arrange - Set mobile viewport
    await Page.SetViewportAsync(new ViewPortOptions
    {
        Width = 375,
        Height = 667
    });

    await NavigateAsync("/blog");
    await WaitAsync(500);

    // Assert - Filter bar should be hidden on mobile
    var filterBarVisible = await EvaluateFunctionAsync<bool>(@"() => {
        const filterBar = document.querySelector('.hidden.lg\\:flex');
        if (!filterBar) return true;
        const rect = filterBar.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
    }");

    Assert.False(filterBarVisible, "Filter bar should be hidden on mobile viewport");
    Output.WriteLine("✅ Filter bar correctly hidden on mobile");

    // Reset viewport
    await Page.SetViewportAsync(new ViewPortOptions
    {
        Width = 1400,
        Height = 900
    });
}

आप व्यूपोर्ट को किसी भी समय बदल सकते हैं, जो प्रतिक्रिया दिखानेवाले ख़ाका को जाँचने के लिए शानदार है. अपने ब्राउज़र विंडो को दस्ती रूप से पुनः आकार देने से बहुत आसान है!

विस्तृत पुंजिलर सुस्पष्ट विशेषताएँ

नेटवर्क इंटरेक्शन

मेरी पसंदीदा विशेषताओं में से एक नेटवर्क निवेदनों को अवरोधित करने और परिवर्धित करने की क्षमता है. यह त्रुटि रपट या ऑफ़लाइन घटना के लिए अमूल्य है:

await Page.SetRequestInterceptionAsync(true);

Page.Request += async (sender, e) =>
{
    // Block all image requests to speed up tests
    if (e.Request.ResourceType == ResourceType.Image)
    {
        await e.Request.AbortAsync();
    }
    // Mock API responses
    else if (e.Request.Url.Contains("/api/posts"))
    {
        await e.Request.RespondAsync(new ResponseData
        {
            Status = HttpStatusCode.OK,
            ContentType = "application/json",
            Body = "{\"posts\": []}"
        });
    }
    else
    {
        await e.Request.ContinueAsync();
    }
};

स्क्रीनशॉट खींच रहे

जब जांच असफल हो, स्क्रीनशॉट एक हजार लॉग संदेश के योग्य है:

try
{
    // Your test code here
    await Page.ClickAsync("#someButton");
}
catch (Exception)
{
    // Take a screenshot on failure
    await Page.ScreenshotAsync("test-failure.png");
    throw; // Re-throw to fail the test
}

पीडीएफ तैयार करना

आप पृष्ठों के पीडीएफ भी तैयार कर सकते हैं, जो कि सर्वर के दोनों ओर का परीक्षण या प्रिंट स्टाइलशीट के लिए उपयोगी है:

await Page.PdfAsync("page.pdf", new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true
});

कोड कवरेज

पुप ब्रेकरपिण्ड जावास्क्रिप्ट कोड विस्तार आंकड़ा जमा कर सकते हैं:

await Page.Coverage.StartJSCoverageAsync();
await Page.GoToAsync("http://localhost:8080");

var coverage = await Page.Coverage.StopJSCoverageAsync();
var totalBytes = coverage.Sum(c => c.Text.Length);
var usedBytes = coverage.Sum(c => c.Ranges.Sum(r => r.End - r.Start));
var percentUsed = usedBytes / (double)totalBytes * 100;

Output.WriteLine($"JavaScript coverage: {percentUsed:F2}%");

ट्रॉपॉप पिक्शन सुस्पष्ट वि.

चलो एक उचित नज़र रखना है कि कैसे पुप्पर अन्य ई2ई परीक्षा औज़ारों के खिलाफ cuuuEEEEC के खिलाफ cucut देता है:

graph TD
    A[E2E Testing Tools] --> B[Selenium WebDriver]
    A --> C[PuppeteerSharp]
    A --> D[Playwright]
    A --> E[Cypress]

    B --> B1[❌ Slow WebDriver protocol]
    B --> B2[❌ Driver management hassle]
    B --> B3[✅ Multi-browser support]
    B --> B4[✅ Mature ecosystem]

    C --> C1[✅ Fast DevTools protocol]
    C --> C2[✅ Auto browser management]
    C --> C3[❌ Chrome/Chromium only]
    C --> C4[✅ Great .NET integration]

    D --> D1[✅ Fast DevTools protocol]
    D --> D2[✅ Auto browser management]
    D --> D3[✅ Multi-browser support]
    D --> D4[⚠️ Newer to .NET ecosystem]

    E --> E1[✅ Great developer experience]
    E --> E2[❌ JavaScript only]
    E --> E3[❌ Not for .NET]
    E --> E4[✅ Excellent documentation]

    style C stroke:#0066cc,stroke-width:3px
    style C1 stroke:#00aa00,stroke-width:2px
    style C2 stroke:#00aa00,stroke-width:2px
    style C4 stroke:#00aa00,stroke-width:2px

सेलेनियम वेबडीriver

पुराना गार्ड

सेलेनियम 2004 से चारों ओर किया गया है और यह दिखाता है. यह प्रौढ़, अच्छी तरह से प्रभावित है, और सूर्य के नीचे हर ब्राउज़र का समर्थन करता है. लेकिन यह भी अपनी उम्र दिखा रहा है:

प्रोविडेंस:

  • सभी ब्राउज़रों का समर्थन करता है (Chroe,fre, lerri, किनारा, मैं, यदि आप एक माचिस्ट हैं)
  • विश्‍व - दर्शन
  • सुप्रसिद्ध तथा व्यापक रूप से गोद लिया गया
  • क्रॉस- ब्राउज़र जांच के लिए अच्छा है

कनेक्शनः

  • वेबDriver प्रोटोकॉल धीमा है
  • ड्राइवर प्रबंधन दर्द है (हालाँकि वेब हार्डवेयर प्रबंधक मदद करता है)
  • 21 साल की एक लड़की की मिसाल लीजिए, जो आज के विकल्पों की तुलना में महसूस करती है
  • हिडीकी जाँच कुछ समय के लिए सामान्य होती है
  • नेटवर्क मोड में कोई अंतर्निर्मित वस्तु नहीं

कब इस्तेमाल किया जाए: जब आप पूरी तरह से कई ब्राउज़रों के पार परीक्षण की जरूरत है, या जब आप पहले से ही से ही सेलेनियमीय पर्यावरण में निवेश कर रहे हैं.

खेलें- दायाँ

नया किड ब्लॉक पर

खेलें- दायाँ Mercuter के लिए माइक्रोसॉफ्ट का जवाब है, के साथ .नेटटी समर्थन प्रारंभ से ही बनाया गया है. यह मूल रूप से पुंजर सुस्पष्ट है लेकिन बहु- ब्राउज़र समर्थन के साथ:

प्रोविडेंस:

  • सी- बस्सिल, फाउंटी, सफरीी (गॉयर) समर्थन देता है
  • नए शब्द का मतलब है, “परमेश्‍वर के साथ एक करीबी रिश्‍ता बनाना । ”
  • स्वचालित ब्राउज़र
  • अंतर्निर्मित नेटवर्क डेकोरेशन, स्क्रीनशॉट, इत्यादि.
  • सुंदर . नेटटी समर्थन

कनेक्शनः

  • नया, इतना छोटा पर्यावरण
  • आप केवल क्रोमोस की जरूरत है तो अधिक है
  • बहु- ब्राउज़र समर्थन के कारण बहुत सा घन सेटअप

कब इस्तेमाल किया जाए: जब आपको बहु- ब्राउज़र समर्थन की आवश्यकता है, लेकिन एक आधुनिक एपीआई की जरूरत है. यदि आप एक नया प्रोजेक्ट प्रारंभ कर रहे हैं और क्रॉस-ब्रोज जांच की जरूरत है, खेल प्रारंभ हो सकता है संभवतः आपका सबसे अच्छा होना चाहिए.

सलीड

जावास्क्रिप्ट डेवलपर डालिंग

Cyred तेज है अगर आप जावास्क्रिप्ट/ क़िस्म स्क्रिप्ट में काम कर रहे हैं, लेकिन यह एक गैर प्रारंभकर्ता है .inT विकासकर्ता:

प्रोविडेंस:

  • कमाल की डेवलपर अनुभव
  • समय क्षेत्र
  • स्वचालित इंतजार कर रहा है
  • बड़ा दस्तावेज़ीकरण

कनेक्शनः

  • जावास्क्रिप्ट/ क़िस्मस्क्रिप्ट
  • कोई .नेटटी समर्थन नहीं
  • बहुत से टैब या विंडो को जाँच नहीं सकता
  • अपने स्वयं के अनुप्रयोग को जाँचने के लिए सीमित करें (कोई भी डोमेन के पार जाँच नहीं)

कब इस्तेमाल किया जाए: नहीं, तुम लिख रहे हैं . NEET कोड. कुछ करने के लिए आगे रखें जो अपने तकनीक स्टैक के साथ एकीकृत है.

तो आपको क्या प्रयोग करना चाहिए?

यहाँ मेरा ले लो:

graph TD
    A[What E2E tool?] --> B{Need multi-browser testing?}
    B -->|Yes| C{Starting new project?}
    B -->|No| D[PuppeteerSharp]

    C -->|Yes| E[Playwright]
    C -->|No| F{Invested in Selenium?}

    F -->|Yes| G[Stick with Selenium]
    F -->|No| E

    D --> H[✅ Fast, simple, reliable]
    E --> I[✅ Modern, flexible]
    G --> J[⚠️ Consider migrating]

    style D stroke:#0066cc,stroke-width:3px
    style H stroke:#00aa00,stroke-width:2px

अधिकतर के लिए.NET विकासकर्ता आधुनिक वेब अनुप्रयोगों को निर्माण करते हैं:

  • क्रोमोस- केवल जाँच? ✔ पैसिव स्मिटर सुस्पष्ट करता है
  • मल्टी- ब्राउज़र परीक्षा? TLSPermissions
  • सेलेनियम का उपयोग पहले से ही? खेलने के लिए भाग रहे हैं पर जल्दी नहीं करते

सीआई/सीडी में जाँच चल रहा है

E2EEE जाँच सभी अच्छी तरह से और आपके स्थानीय मशीन पर अच्छा हैं, लेकिन वे भी सीII/CDs में चलाने की जरूरत है. यहाँ है कि कैसे मैं के लिए कुछ भी सेट किया है Git क्रियाएँ:

name: E2E Tests

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  e2e-tests:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '9.0.x'

    - name: Install dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --no-restore

    - name: Start application
      run: |
        dotnet run --project Mostlylucid/Mostlylucid.csproj &
        echo $! > app.pid

    - name: Wait for application to start
      run: |
        timeout 60 bash -c 'until curl -f http://localhost:8080/health; do sleep 2; done'

    - name: Run E2E tests
      run: |
        dotnet test Mostlylucid.Test/Mostlylucid.Test.csproj \
          --filter "Category=E2E" \
          --logger "console;verbosity=detailed"

    - name: Upload screenshots on failure
      if: failure()
      uses: actions/upload-artifact@v3
      with:
        name: test-screenshots
        path: '**/test-failure-*.png'

    - name: Stop application
      if: always()
      run: |
        kill $(cat app.pid) || true

कुंजी बिट्स:

  1. पृष्ठभूमि में अनुप्रयोग प्रारंभ करें
  2. अच्छी सेहत के लिए इंतज़ार कीजिए ( सेहत जाँच के अंत में)
  3. E2 परीक्षा चलाएँ
  4. यदि कोई जांच असफल हो तो स्क्रीनशॉट अपलोड करें
  5. हमेशा अनुप्रयोग बन्द करें, तब भी जब जांच असफल हो जाए

आम इरादा और उनसे दूर रहना

फ्लैगकी जाँच

E2EEVE परीक्षण कभी-कभी पारित हो सकता है और दूसरों को विफल कर सकते हैं. यह आम तौर पर समय पर समस्याओं के लिए नीचे होता है. यहाँ इन्हें कैसे दूर रहना है:

खराब:

await Page.ClickAsync("#button");
var text = await GetTextContentAsync("#result");
Assert.Equal("Success", text);

अच्छा:

await Page.ClickAsync("#button");
await Page.WaitForSelectorAsync("#result");
var text = await GetTextContentAsync("#result");
Assert.Equal("Success", text);

आप अस्तित्व और दृश्‍य के साथ बातचीत करने के बारे में हमेशा इंतजार करें.

जाँच

हर परीक्षा को पूरी तरह स्वतंत्र होना चाहिए. पिछले जाँच से स्थिति पर भरोसा नहीं है:

खराब:

[Fact]
public async Task Test1_Login()
{
    await LoginAsync("user", "password");
    // User is now logged in for subsequent tests
}

[Fact]
public async Task Test2_ViewDashboard()
{
    // Assumes user is still logged in from Test1
    await NavigateAsync("/dashboard");
}

अच्छा:

[Fact]
public async Task Test1_Login()
{
    await LoginAsync("user", "password");
    await LogoutAsync(); // Clean up
}

[Fact]
public async Task Test2_ViewDashboard()
{
    await LoginAsync("user", "password"); // Set up needed state
    await NavigateAsync("/dashboard");
    await LogoutAsync(); // Clean up
}

पृष्ठ ऑब्जेक्ट पैटर्न

जटिल पृष्ठ के लिए, अपने जांचीय बनाए रखने के लिए पृष्ठ ऑब्जेक्ट पैटर्न का प्रयोग करें:

public class BlogPageObject
{
    private readonly IPage _page;

    public BlogPageObject(IPage page)
    {
        _page = page;
    }

    public async Task SelectLanguageAsync(string language)
    {
        await _page.ClickAsync("#LanguageDropDown button");
        await _page.WaitAsync(300);
        await _page.ClickAsync($"#LanguageDropDown a:has-text('{language}')");
    }

    public async Task<string[]> GetPostTitlesAsync()
    {
        return await _page.EvaluateFunctionAsync<string[]>(@"() => {
            return Array.from(document.querySelectorAll('.post-title'))
                        .map(el => el.textContent.trim());
        }");
    }
}

// Usage in tests
[Fact]
public async Task Can_Filter_By_Language()
{
    var blogPage = new BlogPageObject(Page);
    await NavigateAsync("/blog");

    await blogPage.SelectLanguageAsync("Spanish");
    var titles = await blogPage.GetPostTitlesAsync();

    Assert.All(titles, title => Assert.NotEmpty(title));
}

परफ़ॉर्मेंस पर ध्यान दें

E2EE जाँच इकाई परीक्षण से धीमी हैं, वहाँ कोई आसपास नहीं हो रहा है. लेकिन आप उन्हें तेजी से कर सकते हैं:

समानांतर में जाँच चलाएँ

एक्स इकाई डिफ़ॉल्ट से समानांतर जांच चलाता है, लेकिन आप साझा स्थिति के बारे में सावधान रहने की जरूरत है:

[Collection("E2E Tests")] // Tests in same collection run sequentially
public class FilterBarTests : E2ETestBase
{
    // Tests here share resources
}

[Collection("Blog Tests")] // Different collection runs in parallel
public class BlogTests : E2ETestBase
{
    // Tests here run in parallel with FilterBarTests
}

असक्रिय विशेषताएँ अक्षम करें

गति जांच उन विशेषताओं को निष्क्रिय करता है जिनकी आपको आवश्यकता नहीं है:

Browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = true,
    Args = new[]
    {
        "--no-sandbox",
        "--disable-setuid-sandbox",
        "--disable-dev-shm-usage", // Overcome limited resource problems
        "--disable-accelerated-2d-canvas",
        "--disable-gpu", // Not needed for headless
        "--disable-images", // Don't load images if you don't need them
        "--disable-javascript", // Only if testing static content
    }
});

नेटवर्क इंटरेक्शन बुद्धिमानी से इस्तेमाल करें

तेज चीजों को कम करने के लिए अनावश्यक संसाधन ब्लॉक:

await Page.SetRequestInterceptionAsync(true);
Page.Request += async (sender, e) =>
{
    var blockedResourceTypes = new[]
    {
        ResourceType.Image,
        ResourceType.Media,
        ResourceType.Font,
        ResourceType.StyleSheet // If you don't need to test styling
    };

    if (blockedResourceTypes.Contains(e.Request.ResourceType))
    {
        await e.Request.AbortAsync();
    }
    else
    {
        await e.Request.ContinueAsync();
    }
};

डिबगिंग ई2 परीक्षा

जब जाँच असफल हो जाए तो आपको उन्हें डीबग करना होगा. यहाँ कुछ तकनीकयाँ हैं

गैर- संरक्षक मोड में चलाएँ

सेट Headless = false क्रिया में ब्राउज़र को देखने के लिए:

Browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = false,
    SlowMo = 100, // Slow down by 100ms to see what's happening
});

डीएसए औजार इस्तेमाल करें

आप वास्तव में डीएव औजार प्रोग्राम को खोल सकते हैं:

Browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = false,
    Devtools = true, // Auto-open DevTools
});

कंसोल लॉगिंग

ब्राउज़र से कंसोल संदेश कैप्चर करें:

Page.Console += (sender, e) =>
{
    Output.WriteLine($"Browser console: {e.Message.Text}");
};

अनुरोध किया जा रहा है

सभी नेटवर्क निवेदन लॉग करें:

Page.Request += (sender, e) =>
{
    Output.WriteLine($"Request: {e.Request.Method} {e.Request.Url}");
};

Page.Response += (sender, e) =>
{
    Output.WriteLine($"Response: {e.Response.Status} {e.Response.Url}");
};

वास्तविक विश्‍व जाँच पैटर्न

यहाँ कुछ पैटर्न हैं जो मैं नियमित रूप से अपने E2 परीक्षा में प्रयोग करता हूँ:

फ़ॉर्म जमा करने की जाँच की जा रही है

[Fact]
public async Task Can_Submit_Comment()
{
    await NavigateAsync("/blog/some-post");

    // Fill in the comment form
    await TypeAsync("#comment-name", "Test User");
    await TypeAsync("#comment-email", "[email protected]");
    await TypeAsync("#comment-content", "This is a test comment");

    // Submit the form
    await ClickAsync("#comment-submit");

    // Wait for success message
    await WaitForSelectorAsync(".comment-success");

    // Verify the comment appears
    var commentText = await GetTextContentAsync(".comment-list .comment:last-child .comment-content");
    Assert.Contains("test comment", commentText.ToLower());
}

कुंजीपट इंटरफेस की जाँच की जा रही है

[Fact]
public async Task Can_Navigate_With_Keyboard()
{
    await NavigateAsync("/blog");

    // Focus the search box
    await Page.FocusAsync("#search");

    // Type a search query
    await Page.Keyboard.TypeAsync("testing");

    // Press arrow down to select first result
    await Page.Keyboard.PressAsync("ArrowDown");

    // Press enter to navigate
    await Page.Keyboard.PressAsync("Enter");

    // Verify we navigated to the right page
    await WaitAsync(1000);
    Assert.Contains("/blog/", Page.Url);
}

फ़ाइल अपलोड किया जा रहा है

[Fact]
public async Task Can_Upload_Image()
{
    await NavigateAsync("/admin/upload");

    // Create a test file
    var testFilePath = Path.Combine(Path.GetTempPath(), "test-image.jpg");
    File.WriteAllBytes(testFilePath, new byte[] { 0xFF, 0xD8, 0xFF }); // JPEG header

    // Upload the file
    var fileInput = await Page.QuerySelectorAsync("input[type=file]");
    await fileInput.UploadFileAsync(testFilePath);

    await ClickAsync("#upload-submit");

    // Verify upload succeeded
    await WaitForSelectorAsync(".upload-success");

    // Clean up
    File.Delete(testFilePath);
}

जाँच व छोड़ें

[Fact]
public async Task Can_teAsync("/admin/posts");

    var dragSource = await Page.QuerySelectorAsync(".post-item[data-id='1']");
    var dropTarget = await Page.QuerySelectorAsync(".post-item[data-id='3']");

    var sourceBox = await dragSource.BoundingBoxAsync();
    var targetBox = await dropTarget.BoundingBoxAsync();

    // Perform drag and drop
    await Page.Mouse.MoveAsync(sourceBox.X + sourceBox.Width / 2, sourceBox.Y + sourceBox.Height / 2);
    await Page.Mouse.DownAsync();
    await Page.Mouse.MoveAsync(targetBox.X + targetBox.Width / 2, targetBox.Y + targetBox.Height / 2);
    await Page.Mouse.UpAsync();

    await WaitAsync(500);

    // Verify new order
    var firstItemId = await Page.EvaluateFunctionAsync<string>(
        "() => document.querySelector('.post-item').dataset.id"
    );
    Assert.Equal("1", firstItemId);
}

पीएसई के साथ संयोजन. int कोर जाँच

आप पुपिप सुधारर के साथ सुस्पष्ट कर सकते हैं एनईएस. एक और अधिक एकीकृत जाँच अनुभव के लिए:

public class E2EWebApplicationFactory : WebApplicationFactory<Program>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseUrls("http://localhost:5050");

        builder.ConfigureServices(services =>
        {
            // Override services for testing
            // For example, use in-memory database
            services.RemoveAll<DbContextOptions<MostlylucidDbContext>>();
            services.AddDbContext<MostlylucidDbContext>(options =>
            {
                options.UseInMemoryDatabase("TestDb");
            });
        });
    }
}

public abstract class IntegratedE2ETestBase : E2ETestBase, IClassFixture<E2EWebApplicationFactory>
{
    protected E2EWebApplicationFactory Factory { get; }

    protected IntegratedE2ETestBase(E2EWebApplicationFactory factory, ITestOutputHelper output)
        : base(output)
    {
        Factory = factory;
    }

    public override async Task InitializeAsync()
    {
        await base.InitializeAsync();

        // Application is automatically started by WebApplicationFactory
        // Override BaseUrl to use the factory's address
        BaseUrl = "http://localhost:5050";
    }
}

पीडीएफ़ बनाने तथा स्वचालित परीक्षण के लिए जाँच - पूर्व पुख्ता

Went E2 परीक्षण शानदार है, pucutiocize एक सन सेना चाकू है जो अब तक और अधिक कर सकते हैं। इसके सबसे लोकप्रिय उपयोगों में से एक है वेब सामग्री - यह नहीं मिल गया है, हालांकि इसके बिना नहीं मिल गया है. यदि आप निर्माण कर रहे हैं, रिपोर्ट, या कोई भी पीढ़ी के दस्तावेज़, इस प्रणाली के भाग को बचाने के लिए आप घंटे का उपयोग कर सकते हैं।

पीडीएफ तैयार करना - प्रतिज्ञा और दर्द

विचार सरल है: क्रोमोस में एक वेब पृष्ठ रेंडर करो और इसे पीडीएफ के रूप में सहेजें. रिपोर्ट, प्रमाणपत्रों, या कोई गतिशील सामग्री जो PDF फ़ॉर्मेट में वितरित की जानी है.

यहाँ बुनियादी तरीका है:

public class PdfGeneratorService
{
    public async Task<byte[]> GeneratePdfFromUrlAsync(string url)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true,
            Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
        });

        await using var page = await browser.NewPageAsync();
        await page.GoToAsync(url, new NavigationOptions
        {
            WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
        });

        var pdfData = await page.PdfDataAsync(new PdfOptions
        {
            Format = PaperFormat.A4,
            PrintBackground = true,
            MarginOptions = new MarginOptions
            {
                Top = "20mm",
                Right = "20mm",
                Bottom = "20mm",
                Left = "20mm"
            }
        });

        return pdfData;
    }
}

मृत सरल लग रहा है, है ना?

PDF पीढ़ी मिल गया है - क्या कोई आपको बताता है

1. फ़ॉन्ट एम्बेडिंग दुःस्वप्न

समस्या: आपका सुंदर कस्टम फ़ॉन्ट PDF, या बदतर में नहीं दिखता है, वे वहाँ हैं लेकिन पूरी तरह से बेकार लग रहे हैं.

ऐसा क्यों होता है: क्रोमा को PDF बनाने के दौरान फ़ॉन्ट फ़ाइलों पर पहुँच की आवश्यकता है. यदि आपका फ़ॉन्ट बाहरी सीडीN और क्रोमा से लोड हो गया है तो उन्हें (इंजन, नेटवर्क समस्याएँ, समय पर) पहुँच नहीं सकता.

समाधानः

await page.GoToAsync(url, new NavigationOptions
{
    WaitUntil = new[]
    {
        WaitUntilNavigation.Networkidle0,  // Wait for network to be idle
        WaitUntilNavigation.Load           // Wait for fonts to load
    },
    Timeout = 60000  // Give it time to load fonts
});

// Extra insurance - wait for fonts to actually load
await page.EvaluateFunctionAsync(@"async () => {
    await document.fonts.ready;
}");

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true  // CRUCIAL for @font-face fonts
});

बेहतर है, अपने फ़ॉन्ट स्थानीय होस्ट या अपने सीएसएस में बेस64 के रूप में उन्हें स्कैन. हाँ, यह एक faffff है, लेकिन यह भरोसेमंद है.

2. सीएसएस प्रिंट मीडिया क्वाटर

समस्या: आपका पीडीएफ आपके वेब पृष्ठ की तरह कुछ नहीं दिखता है क्योंकि क्रोमोला प्रिंट मीडिया को लागू करता है.

यह वास्तव में है सही व्यवहार - पीडीएफ मीडिया प्रिंट कर रहे हैं. लेकिन यह हर कोई पहली बार बाहर ले जाता है.

समाधानः

प्रयोक्ता @media print सीएसएस नियम उचित:

/* Show on screen, hide in PDF */
.no-print {
    display: block;
}

@media print {
    .no-print {
        display: none !important;
    }

    /* Prevent page breaks inside elements */
    .keep-together {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    /* Force page breaks */
    .page-break {
        page-break-before: always;
    }
}

या, यदि आप चाहते हैं कि स्क्रीन संस्करण आपके पीडीएफ में उपलब्ध हो (" स्क्रीन के आकार को पीडीएफ के रूप में तैयार करने के लिए प्रयोग किया जाए)

await page.EmulateMediaTypeAsync(MediaType.Screen);  // Force screen media
var pdfData = await page.PdfDataAsync();

3 पेज - आपके वजूद के बारे में चर्चा कीजिए ।

समस्या: इस बारे में ज़्यादा जानने के लिए आप क्या कर सकते हैं?

होकर रहनेवाली! आप क्रोमोस के आंतरिक पटिंग एल्गोरिथ्म के खिलाफ लड़ रहे हैं, और यह समय की सबसे ज्यादा जीतने के लिए जा रहा है.

आप क्या कर सकते हैं:

@media print {
    h1, h2, h3, h4, h5, h6 {
        page-break-after: avoid;
        break-after: avoid;
    }

    table, figure, img {
        page-break-inside: avoid;
        break-inside: avoid;
    }

    /* Force specific breaks */
    .new-page {
        page-break-before: always;
    }
}

और अपने pueppeeoctor में:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true,
    PreferCSSPageSize = true,  // Respect CSS @page rules
    DisplayHeaderFooter = false
});

डाउनलोड करें: जटिल लेआउटों के लिए, कभी कभी कभी - कभी अपने एचटीएमएल का निर्माण करना आसान होता है

<div class="page">
    <!-- First page content -->
</div>
<div class="page-break"></div>
<div class="page">
    <!-- Second page content -->
</div>

4 शीर्षक और पाद- सूचना - आप सोचते हैं से अधिक जटिल

आप शीर्ष और पाद- सूचना जोड़ सकते हैं, लेकिन एपीआई एक सा हलकी है:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Format = PaperFormat.A4,
    DisplayHeaderFooter = true,
    HeaderTemplate = @"
        <div style='font-size: 10px; text-align: center; width: 100%;'>
            <span class='title'></span>
        </div>
    ",
    FooterTemplate = @"
        <div style='font-size: 10px; text-align: center; width: 100%;'>
            Page <span class='pageNumber'></span> of <span class='totalPages'></span>
        </div>
    ",
    MarginOptions = new MarginOptions
    {
        Top = "30mm",     // Must be larger to accommodate header
        Bottom = "25mm"   // Must be larger to accommodate footer
    }
});

मिल गया:

  • शीर्ष/ मह्त्वपूर्ण टैम्प्लेट वैध होना चाहिए एचटीएमएल परंतु कोई बाहरी सीएसएस नहीं, कोई जावास्क्रिप्ट नहीं
  • आप केवल विशेष चर प्राप्त करते हैं: date, title, url, pageNumber, totalPages
  • केवल स्टाइल ही इनलाइन है
  • हाशिया काफी बड़ा होना चाहिए यदि आप हेडर को/ पहले से ही रख सकते हैं या वे आपकी सामग्री को आपस में जोड़ कर रख सकते हैं

पृष्ठभूमि ग्राफिक्स 5.

डिफ़ॉल्ट से क्रोमा पृष्ठभूमि चित्र या रंग मुद्रित नहीं करता (यह स्याही सहेजने के लिए ब्राउज़र डिफ़ॉल्ट है). आप जरूर इसे सक्षम करें:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    PrintBackground = true  // Without this, your beautiful backgrounds vanish
});

6 दिखाया गया मेमोरी बड़े दस्तावेज़ के साथ

समस्या: बहुत से PDF बनाने पर आपका अनुप्रयोग की स्मृति गुब्बारे में बनती है और अंत में क्रैश हो जाता है.

क्यों: प्रत्येक ब्राउज़र उदाहरण महत्वपूर्ण स्मृति (100-200MB) का उपयोग करता है, और यदि आप सही तरह से कटौती नहीं कर रहे हैं, वे ऊपर.

समाधानः

हमेशा इस्तेमाल करें await using या सही अनुवाद:

// Good - automatic disposal
await using var browser = await Puppeteer.LaunchAsync(options);
await using var page = await browser.NewPageAsync();

// Or manually
IBrowser? browser = null;
try
{
    browser = await Puppeteer.LaunchAsync(options);
    // ... use browser
}
finally
{
    if (browser != null)
    {
        await browser.CloseAsync();
        await browser.DisposeAsync();
    }
}

उच्च-oooox PDF पीढ़ी के लिए, ब्राउज़र उदाहरणों के लिए फिर से विचार करें:

public class PdfGeneratorService : IDisposable
{
    private IBrowser? _browser;
    private readonly SemaphoreSlim _semaphore = new(1, 1);

    public async Task<byte[]> GeneratePdfAsync(string url)
    {
        await _semaphore.WaitAsync();
        try
        {
            // Reuse browser instance
            _browser ??= await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true
            });

            await using var page = await _browser.NewPageAsync();
            await page.GoToAsync(url);
            return await page.PdfDataAsync();
        }
        finally
        {
            _semaphore.Release();
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (_browser != null)
        {
            await _browser.CloseAsync();
            await _browser.DisposeAsync();
        }
        _semaphore.Dispose();
    }

    public void Dispose()
    {
        DisposeAsync().AsTask().Wait();
    }
}

7. स्केल विकल्प - छोटा पाठ, अधिक सामग्री

कभी - कभी आपको पृष्ठ पर ज़्यादा संतुष्ट होने की ज़रूरत है:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Format = PaperFormat.A4,
    Scale = 0.8m,  // 80% scale - fits more content
    PrintBackground = true
});

लेकिन बहुत छोटा है और यह अपठित है सावधान रहना.

वास्तविक विश्वव्यापी पीडीएफ निर्माण पैटर्न

यहाँ है कि कैसे मैं वास्तव में उत्पादन में PDF पीढ़ी करते हैं:

public class InvoicePdfGenerator
{
    private readonly ILogger<InvoicePdfGenerator> _logger;

    public InvoicePdfGenerator(ILogger<InvoicePdfGenerator> logger)
    {
        _logger = logger;
    }

    public async Task<byte[]> GenerateInvoicePdfAsync(Invoice invoice)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true,
            Args = new[]
            {
                "--no-sandbox",
                "--disable-setuid-sandbox",
                "--disable-dev-shm-usage"  // Overcome limited resource problems
            }
        });

        await using var page = await browser.NewPageAsync();

        // Set up console logging to debug issues
        page.Console += (_, e) =>
        {
            _logger.LogInformation("Browser console: {Message}", e.Message.Text);
        };

        try
        {
            // Generate HTML content (using Razor, or however you do it)
            var htmlContent = await GenerateInvoiceHtmlAsync(invoice);

            // Set content directly rather than navigating to URL
            await page.SetContentAsync(htmlContent, new NavigationOptions
            {
                WaitUntil = new[] { WaitUntilNavigation.Networkidle0 }
            });

            // Wait for fonts to load
            await page.EvaluateFunctionAsync("() => document.fonts.ready");

            // Force screen media type to avoid print media queries changing layout
            await page.EmulateMediaTypeAsync(MediaType.Screen);

            // Generate PDF
            var pdfData = await page.PdfDataAsync(new PdfOptions
            {
                Format = PaperFormat.A4,
                PrintBackground = true,
                MarginOptions = new MarginOptions
                {
                    Top = "10mm",
                    Right = "10mm",
                    Bottom = "10mm",
                    Left = "10mm"
                },
                PreferCSSPageSize = false
            });

            _logger.LogInformation("Generated PDF for invoice {InvoiceId}, size: {Size} bytes",
                invoice.Id, pdfData.Length);

            return pdfData;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to generate PDF for invoice {InvoiceId}", invoice.Id);

            // Take a screenshot for debugging
            try
            {
                var screenshot = await page.ScreenshotDataAsync();
                _logger.LogWarning("Captured screenshot of failed PDF generation: {Size} bytes",
                    screenshot.Length);
                // Could save this to blob storage for debugging
            }
            catch
            {
                // Swallow screenshot errors
            }

            throw;
        }
    }

    private async Task<string> GenerateInvoiceHtmlAsync(Invoice invoice)
    {
        // Your HTML generation logic here
        // Could use Razor views, or any templating engine
        return $@"
<!DOCTYPE html>
<html>
<head>
    <meta charset='utf-8'>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');

        body {{
            font-family: 'Inter', sans-serif;
            margin: 0;
            padding: 20px;
            color: #333;
        }}

        @media print {{
            .page-break {{
                page-break-before: always;
            }}

            .no-break {{
                page-break-inside: avoid;
            }}
        }}
    </style>
</head>
<body>
    <div class='no-break'>
        <h1>Invoice #{invoice.Number}</h1>
        <p>Date: {invoice.Date:yyyy-MM-dd}</p>
    </div>

    <!-- Invoice content -->
</body>
</html>";
    }
}

आड़ा वि. Permissions

सरल लेकिन अकसर आवश्यक होता है:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Format = PaperFormat.A4,
    Landscape = true,  // Horizontal orientation
    PrintBackground = true
});

विशेष पृष्ठ आकार

मानक फॉर्मेटों में सीमित नहीं:

var pdfData = await page.PdfDataAsync(new PdfOptions
{
    Width = "210mm",   // Custom width
    Height = "297mm",  // Custom height (this is A4, but you can use any size)
    PrintBackground = true
});

पुख्ता करने के लिए दूसरे व्यावहारिक इस्तेमाल

परीक्षण और PDF पीढ़ी के अलावा, uzeppirer कई अन्य अस्थिर कार्यों पर तेजी से धीमा. चलो सबसे सामान्य वास्तव में असली विश्व अनुप्रयोगों की जाँच करें.

डाटा निकाला जा रहा है

puecuter सुस्पष्ट जावास्क्रिप्ट-भारी साइटों के लिए शानदार है जहाँ पारंपरिक HTML पारर कम हो जाता है:

public class ProductScraper
{
    public async Task<List<Product>> ScrapeProductsAsync(string url)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();
        await page.GoToAsync(url, new NavigationOptions
        {
            WaitUntil = new[] { WaitUntilNavigation.Networkidle2 }
        });

        // Wait for products to render (adjust selector as needed)
        await page.WaitForSelectorAsync(".product-item");

        // Extract product data using JavaScript
        var products = await page.EvaluateFunctionAsync<List<Product>>(@"() => {
            return Array.from(document.querySelectorAll('.product-item')).map(item => ({
                name: item.querySelector('.product-name')?.textContent?.trim(),
                price: parseFloat(item.querySelector('.product-price')?.textContent?.replace('£', '')),
                imageUrl: item.querySelector('img')?.src,
                inStock: !item.querySelector('.out-of-stock')
            }));
        }");

        return products;
    }
}

इसे कब इस्तेमाल किया जाए:

  • एकल- प्रतीक अनुप्रयोग (Rue, Vue, कोणीय)
  • असीमित लपेट या आलसी लोड करने के साथ प्रसंग
  • जब आपको पृष्ठ के साथ वार्तालाप करने की जरूरत है ( बटनों को क्लिक करें, फ़ॉर्म भरने से पहले भरें)
  • लॉगइन दीवार के पीछे विषयवस्तु

कब नहीं इस्तेमाल करें:

  • सरल स्थिर एचटीएमएल फ़ाइलें Hatatatacks या कोण सुस्पष्ट बदले में - बहुत तेज़ और हल्का)
  • उच्च-तम जांच (बस्तर सिर महत्त्वपूर्ण है)
  • जब वहाँ एक एपीआई उपलब्ध है (हमेशा के लिए अधिकारी एपीआई एपीआई से अधिक पसंद करते हैं!)

स्क्रीनशॉट तैयार किया जा रहा है

जांच के अलावा, स्क्रीनशॉट लघु छवि, पूर्वावलोकन, या अभिलेखीकरण के लिए उपयोगी हैं:

public class ScreenshotService
{
    public async Task<byte[]> CaptureWebsiteAsync(string url, int width = 1920, int height = 1080)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();
        await page.SetViewportAsync(new ViewPortOptions
        {
            Width = width,
            Height = height
        });

        await page.GoToAsync(url, new NavigationOptions
        {
            WaitUntil = new[] { WaitUntilNavigation.Networkidle2 }
        });

        // Full page screenshot
        return await page.ScreenshotDataAsync(new ScreenshotOptions
        {
            FullPage = true,
            Type = ScreenshotType.Png
        });
    }

    public async Task<byte[]> CaptureElementAsync(string url, string selector)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();
        await page.GoToAsync(url);

        var element = await page.WaitForSelectorAsync(selector);
        if (element == null)
        {
            throw new InvalidOperationException($"Element {selector} not found");
        }

        // Screenshot of specific element
        return await element.ScreenshotDataAsync();
    }
}

व्यावहारिक उपयोग:

  • ब्लॉग पोस्टों के लिए छवि टैग बनाया जा रहा है:
  • वेबसाइटों के लिए लघु छवि बनाया जा रहा है
  • अनुपालन के लिए वेब पृष्ठ अभिलेख प्राप्त किया जा रहा है
  • लिंक साझेदारी के लिए पूर्वावलोकन छवियों को बनाया जा रहा है

परफ़ॉर्मेंस मॉनीटर

पृष्ठ लोड प्रदर्शन मापें:

public class PerformanceMonitor
{
    public async Task<PerformanceMetrics> MeasurePagePerformanceAsync(string url)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();

        var stopwatch = Stopwatch.StartNew();
        await page.GoToAsync(url, new NavigationOptions
        {
            WaitUntil = new[] { WaitUntilNavigation.Networkidle2 }
        });
        stopwatch.Stop();

        // Get performance metrics from the browser
        var metrics = await page.MetricsAsync();

        // Get performance timing data
        var performanceTiming = await page.EvaluateExpressionAsync<PerformanceTiming>(@"
            JSON.parse(JSON.stringify(performance.timing))
        ");

        return new PerformanceMetrics
        {
            TotalLoadTime = stopwatch.ElapsedMilliseconds,
            DomContentLoaded = performanceTiming.DomContentLoadedEventEnd - performanceTiming.NavigationStart,
            FirstPaint = metrics["FirstPaint"],
            LayoutCount = (int)metrics["LayoutCount"],
            ScriptDuration = metrics["ScriptDuration"]
        };
    }
}

रिपोर्ट तैयार की गई बनाना

पीडीएफ़ बनाने के लिए पीडीएफ़ पीढ़ी के साथ सम्मिलित एचटीएमएल अस्थायीीकरण:

public class MonthlyReportGenerator
{
    public async Task<byte[]> GenerateMonthlyReportAsync(ReportData data)
    {
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();

        // Generate HTML report using your preferred templating engine
        var html = GenerateReportHtml(data);
        await page.SetContentAsync(html);

        // Wait for any charts to render (if using Chart.js, D3.js, etc.)
        await Task.Delay(2000);

        return await page.PdfDataAsync(new PdfOptions
        {
            Format = PaperFormat.A4,
            PrintBackground = true,
            DisplayHeaderFooter = true,
            HeaderTemplate = $@"
                <div style='font-size: 9px; margin: 0 auto; text-align: center;'>
                    Monthly Report - {data.Month:MMMM yyyy}
                </div>
            ",
            FooterTemplate = @"
                <div style='font-size: 9px; margin: 0 auto; text-align: center;'>
                    Page <span class='pageNumber'></span> of <span class='totalPages'></span>
                </div>
            ",
            MarginOptions = new MarginOptions
            {
                Top = "25mm",
                Bottom = "20mm",
                Left = "15mm",
                Right = "15mm"
            }
        });
    }
}

" फ्री" PDF बनाने का कीमत

अब, यहाँ एक बात है जो PDF पीढ़ी के लिए ट्रिपम्प का उपयोग करने के बारे में है - यह इस अर्थ में है कि आप एक PDF लाइब्रेरी लाइसेंस के लिए भुगतान नहीं करते, लेकिन यह है संसाधन के शर्तों पर मुक्त नहीं.

प्रत्येक ब्राउज़र उदाहरण:

  • रैम के 100-200MB इस्तेमाल करता है
  • रेंडरिंग के लिए आवश्यक महत्वपूर्ण सीपीयू
  • पीडीएफ तैयार करने के लिए 2- 5 सेकण्ड लेता है (अनुप्रयोग पर कार्य करता है)

इस से तुलना करें जैसे PDF लाइब्रेरी्स को:

  • आई- पाठ (पहले तो मैं पाठ सुस्पष्ट) - व्यावसायिक लाइसेंस की आवश्यकता (0.5,500000/ वर्ष), लेकिन छोटी मेमोरी छपाई के साथ PDF बनाता है
  • क्वीप पीडीएफName - एमआईटी लाइसेंस के तहत मुक्त और खुला स्रोत, सी# C# एचटीएमएल कोड (कोई एचटीएमएल नहीं) से पीडीएफ बनाता है
  • Pdf सम कोर - मुक्त गंदगी लाइसेंस, लेकिन क्षमता में अधिक सीमित

जब PDF के लिए पैसिव स्वचालित इस्तेमाल किया जाए:

  • आपके पास पहले से ही एचटीएमएल टैम्प्लेट हैं तथा PDF ख़ाका कोड में फिर से लिखना नहीं चाहते
  • आपको जटिल वेब ख़ाका का पिक्सेल- उत्तम रेंडरिंग चाहिए
  • आवाज़ कम है (< 100 पीडीएफ प्रति घंटे)
  • आप बाहरी वेबसाइटों से PDF बनाने की जरूरत है आप नियंत्रण नहीं है

समर्पित पीडीएफ लाइब्रेरी इस्तेमाल करने के दौरान:

  • उच्च आयतन पीढ़ी (> 100 पीडीएफ प्रति घंटे)
  • सरल ख़ाका (टेलन, प्राप्ति, रपटों में)
  • रिसोर्स- स्कैन्ड एनवायरनमेंट
  • आपको विस्तृत PDF फीचर की आवश्यकता है (फ़ॉर्म्स, हस्ताक्षर, एनक्रिप्शन)

एक हाइब्री के पास

कभी - कभी सबसे अच्छा समाधान दोनों का उपयोग कर रहा है:

public class PdfService
{
    private readonly ILogger<PdfService> _logger;

    public async Task<byte[]> GeneratePdfAsync(PdfRequest request)
    {
        // Simple documents - use QuestPDF (fast, low resources)
        if (request.IsSimpleLayout)
        {
            return GenerateWithQuestPdf(request);
        }

        // Complex documents with web content - use PuppeteerSharp
        return await GenerateWithPuppeteerAsync(request);
    }

    private byte[] GenerateWithQuestPdf(PdfRequest request)
    {
        // QuestPDF code here - much faster for simple layouts
        return Document.Create(container =>
        {
            container.Page(page =>
            {
                page.Size(PageSizes.A4);
                page.Margin(2, Unit.Centimetre);
                page.Content().Text(request.Content);
            });
        }).GeneratePdf();
    }

    private async Task<byte[]> GenerateWithPuppeteerAsync(PdfRequest request)
    {
        // PuppeteerSharp code for complex layouts
        await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
        {
            Headless = true
        });

        await using var page = await browser.NewPageAsync();
        await page.SetContentAsync(request.HtmlContent);
        return await page.PdfDataAsync();
    }
}

कंटेनमेंट

P2EENTEENT परीक्षण के लिए एक पूर्ण खेल - परिवर्तनक किया गया है मेरे . यह सेलेनियम की तुलना में तेजी से है, एक अधिक आधुनिक एपीआई है, और आम तौर पर एक टन का परीक्षण करता है.

मैं यहाँ क्या सुझाव होगा:

  1. पैसिव स्मिटर के साथ प्रारंभ करें यदि आप केवल क्रोमोस/Chpte परीक्षा कर रहे हैं. यह विकल्प की तुलना में सरल और तेजी से है.

  2. बजाएँ- दायाँ इस्तेमाल करें (w) यदि आप बहु-ब्रर समर्थन की जरूरत है. यह Puepepeeuer cugorors के सभी लाभ मिल गया है तेज प्लस फाउंट और lowiriririr.

  3. सेलेनियम से दूर रहें नई परियोजना के लिए जब तक आप इसे उपयोग करने के लिए एक विशेष कारण है (जैसे कि IE11 समर्थन, जो आप उम्मीद नहीं है).

  4. परख - शक्‍ति विकसित कीजिए. E2EE जाँच धीमी होती है और गंभीर उपयोक्ता यात्रा के लिए उन्हें उपयोग करें, हर छोटे विवरण की जाँच के लिए नहीं. यही इकाई और एकीकरण जाँच के लिए कर रहे हैं.

  5. परीक्षाएँ दूर रखे रहेंहर जाँच को अपने स्वयं के आंकड़ा और अपने बाद साफ करना चाहिए.

  6. सहायक विधियाँ इस्तेमाल करें उत्कृष्ट रूप से. आधार वर्ग नमूना मैंने दिखाया कि अपने वास्तविक परीक्षण कोड साफ है और आप क्या परीक्षण कर रहे हैं पर ध्यान केंद्रित, नहीं कैसे आप इसे जाँच कर रहे हैं.

E2EE परीक्षण के लिए दुख की ज़रूरत नहीं है. सही औज़ारों और पैटर्न के साथ, यह वास्तव में काफी सुखद हो सकता है. Tiropir एक ले लो अपने अगले परियोजना पर एक जाना - मुझे लगता है कि आप सुखद रूप से चकित हो जाएगा.

ठीक है, मैं और परीक्षण लिखने के लिए बंद कर रहा हूँ. धन्य जाँच!

आगे पढ़ा जा रहा है

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.