npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

cypress-solid

v0.1.4

Published

Cypress Plugin to test applications using the Solid Protocol

Downloads

58

Readme

Cypress Solid CI

Cypress Plugin to test applications using the Solid Protocol.

Installation

Install the package using npm:

npm install cypress-solid --save-dev

Once it is installed, you'll need to call it in your support file. For example, in cypress/support/e2e.ts:

import 'cypress-solid/support';

You'll also need to set up the node events in the configuration. For example, in cypress.config.ts:

import { defineConfig } from 'cypress';
import { setupSolidNodeEvents } from 'cypress-solid/config';

export default defineConfig({
    e2e: {
        setupNodeEvents: setupSolidNodeEvents,
    },
});

Solid Server

This plugin assumes that you have an instance of the Community Solid Server running (with version 7.0.0 or newer).

To install it, you can run the following command:

npm install @solid/community-server@^7 --save-dev

The server must be running whilst Cypress tests your application, so you may want to use something like the start-server-and-test and concurrently packages to launch both your application and the server. For example, you can configure the following scripts in your package.json:

"scripts": {
    "cy:dev": "concurrently --kill-others \"npm run test:serve-app\" \"npm run test:serve-pod\" \"npm run cy:open\"",
    "cy:open": "cypress open --e2e --browser chromium",
    "cy:run": "cypress run",
    "cy:test": "start-server-and-test test:serve-app http-get://localhost:5001 test:serve-pod http-get://localhost:3000 cy:run",
    "test:serve-app": "vite --port 5001",
    "test:serve-pod": "community-solid-server -p 3000 -l warn"
}

With these, you should be able to run your Cypress tests with npm run cy:test, or open the Cypress UI for local development with npm run cy:dev.

In this example, the application is served using Vite, but you should be able to use the same approach for any other tools. You can learn more about this in the official Cypress documentation: Boot your server.

The default configuration assumes that the server is running on http://localhost:3000, but you can customize this behaviour changing the serverUrl config option.

Basic Usage

In tests where you want to interact with the Solid server, you should call cy.solidReset() before starting. It is important to do it before, and not after, given that if the test fails or is closed unexpectedly, the clean up won't be executed.

This command will prepare the account in the server, and you can complete the log in form during the authentication process using the cy.solidLogin() command.

Finally, there are some helper functions you can use in order to get dynamic data at runtime, such as webId(). This is ideal to avoid hard-coding values that depend on the configuration.

Here's a full example illustrating the basic usage:

import { webId } from 'cypress-solid';

it('Logs in', () => {
    // Prepare the POD.
    cy.solidReset();

    // Open the application and trigger the log in process.
    cy.visit('/');
    cy.get('[aria-label="Login url"]').type(webId()).type('{enter}');

    // Log in to the POD.
    cy.solidLogin();

    // Continue with your test...
});

For a complete example with more advanced functionality, you can check out the Cypress Solid Sandbox sample application.

Configuration

In order to customize the plugin's behaviour, you can pass some configuration options to the setupSolidNodeEvents method:

import { defineConfig } from 'cypress';
import { setupSolidNodeEvents } from 'cypress-solid/config';

export default defineConfig({
    e2e: {
        setupNodeEvents(on, config) {
            setupSolidNodeEvents(on, config, {
                serverUrl: 'http://localhost:1234',
                account: 'bob',
                // ...
            });

            // IMPORTANT Return the updated config object.
            return config;
        },
    },
});

Here's the complete list of available options:

| Option | Default | Description | | --------- | ------------------------- | ----------- | | serverUrl | 'http://localhost:3000' | Url where the Solid server is running. | | account | 'alice' | Account name, this will be used to create a new account in the Solid server. For example, http://localhost:3000/alice/. | | name | 'Alice Cooper' | Display name, this will be declared as foaf:name in the user's profile. | | email | '[email protected]' | Account email, this can be used to log in. | | password | 'secret' | Account password, this can be used to log in. |

Commands

cy.fixtureWithReplacements(path, replacements)

Loads a fixture file with replacements applied.

Arguments

| Name | Default | Description | | ------------ | ------- | ----------- | | path | - | Fixture file path, relative to your fixtures folder. | | replacements | {} | Object with replacements to apply on the fixture. Learn more about replacements. |

Yields

The contents of the fixture with the replacements applied.

cy.solidCreateContainer(path, name?)

Creates a container in the POD.

Arguments

| Name | Default | Description | | ---- | ------------- | ----------- | | path | - | Relative container path. For example, to create a container at http://localhost:3000/alice/tasks/, you would use '/tasks/'. | | name | 'Container' | Container name, declared as rdfs:label in the document's metadata. |

Yields

Nothing.

cy.solidCreateDocument(path, turtleOrFixture, replacements?)

Creates a document in the POD.

Arguments

| Name | Default | Description | | --------------- | ------------- | ----------- | | path | - | Relative document path. For example, to create a document at http://localhost:3000/alice/tasks/1, you would use '/tasks/1'. | | turtleOrFixture | - | Contents of the document in Turtle format, or the path to a Cypress fixture including the contents. For example, for a fixture at cypress/fixtures/task.ttl you would use 'task.ttl'. | | replacements | {} | Object with replacements to apply on the contents before creating the document. Learn more about replacements. |

Yields

Nothing.

cy.solidDeleteDocument(path)

Deletes a document in the POD.

Arguments

| Name | Default | Description | | ------------ | ------------- | ----------- | | path | - | Relative document path. For example, to delete the document at http://localhost:3000/alice/tasks/1, you would use '/tasks/1'. |

Yields

Nothing.

cy.solidAuthorize()

Accepts the authorization form. This command is usually necessary when you have already logged in to your POD and you're reconnecting after a page reload.

Arguments

None.

Yields

Nothing.

cy.solidLogin()

Authenticates using the test account indicated in the configuration. This command should be called once the authentication process has been started and the application has been redirected to the identity provider.

Arguments

None.

Yields

Nothing.

cy.solidReadDocument(path)

Reads a document in the POD.

Arguments

| Name | Default | Description | | ------------ | ------------- | ----------- | | path | - | Relative document path. For example, to read the document at http://localhost:3000/alice/tasks/1, you would use '/tasks/1'. |

Yields

The contents of the Solid document in Turtle format.

cy.solidRequest(input, init?)

Makes an authenticated request to the Solid server.

Arguments

Takes the same arguments as Node's built-in fetch().

Yields

An object with the following fields:

| Name | Type | Description | | ------------ | ---- | ----------- | | headers | Headers | Response headers. | | status | number | Response status code. | | statusText | string | Response status message. | | body | string | Response body. |

cy.solidReset()

Creates the account in the server if it didn't exist, and resets the content in the POD to its initial state.

Arguments

None.

Yields

Nothing.

cy.solidUpdateDocument(path, sparqlOrFixture, replacements?)

Updates a document in the POD.

Arguments

| Name | Default | Description | | --------------- | ------------- | ----------- | | path | - | Relative document path. For example, to update a document at http://localhost:3000/alice/tasks/1, you would use '/tasks/1'. | | sparqlOrFixture | - | SPARQL updates to apply in the document, or the path to a Cypress fixture including the query. For example, for a fixture at cypress/fixtures/update-task.sparql you would use 'update-task.sparql'. | | replacements | {} | Object with replacements to apply on the query before creating the document. Learn more about replacements. |

Yields

Nothing.

Helpers

You can also use some of these helper functions in your tests. These should be imported directly from cypress-solid, like this:

import { webId } from 'cypress-solid';

it('See tasks container url', () => {
    cy.contains(podUrl('/tasks/'));
});

config(key?)

Get configuration values.

Arguments

| Name | Default | Description | | ---- | ------- | ----------- | | key | - | Name of the config value to (serverUrl, account, etc.). If this argument is omitted, the whole configuration object will be returned. |

Returns

Value of the configuration option if key was provided, or the entire configuration object otherwise.

podUrl(path?)

Resolve relative paths in the test account to get the full url.

Arguments

| Name | Default | Description | | ---- | ------- | ----------- | | path | '' | Relative path of the url to resolve. |

Returns

Url for the given path. For example, for the '/tasks/' path with the default configuration you would get 'http://localhost:3000/alice/tasks/'.

serverUrl(path?)

Resolve relative paths in the server to get the full url.

Arguments

| Name | Default | Description | | ---- | ------- | ----------- | | path | '' | Relative path of the url to resolve. |

Returns

Url for the given path. For example, for the '/.account/' path with the default configuration you would get 'http://localhost:3000/.account/'.

webId()

Get test account webId. You can use this to log in in your application without hard-coding any urls.

Arguments

None.

Returns

WebId for the test account. For example, with the default configuration you would get 'https://localhost:3000/alice/profile/card#me'.

Assertions

This plugin also adds the following Chai Assertions. All of them can also use wildcards.

sparql

Use it to compare SPARQL strings. For example:

it('Compares SPARQL', () => {
    cy.intercept('PATCH', podUrl('/tasks/1')).as('updateTask');

    // Test your application...

    cy.get('@updateTask')
        .its('request.body')
        .should(
            'be.sparql',
            `
                DELETE DATA {
                    <#it> <https://schema.org/name> "Previous task name" .
                } ;
                INSERT DATA {
                    <#it> <https://schema.org/name> "New task name" .
                } .
            `
        );
});

turtle

Use it to compare Turtle strings. For example:

it('Compares Turtle', () => {
    cy.intercept('PUT', podUrl('/tasks/*')).as('createTask');

    // Test your application...

    cy.get('@createTask')
        .its('request.body')
        .should(
            'be.turtle',
            `
                @prefix schema: <https://schema.org/> .

                <#it>
                    a schema:Action ;
                    schema:name "Learn to use cypress-solid" .
           `
        );
});

Replacements and wildcards

There are some situations when seeding data or making assertions where it may be impossible to know some values before runtime. For example, seeding some data that depends on ids generated dynamically, or making assertions in a document using dates and timestamps.

For these scenarios, some of the methods in this plugin accept replacements and wildcards.

Replacements

Replacements are placeholders in fixture files that can be replaced once they are loaded in your tests. They use the {{placeholder}} syntax.

For example, imagine that you have the following fixture to represent an update to a Movie document fixtures/watch-movie.sparql:

INSERT DATA {
    @prefix schema: <https://schema.org/>.
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

    <#watch-action>
        a schema:WatchAction ;
        schema:object <#it> ;
        schema:startTime "{{date}}"^^xsd:dateTime .
}

This fixture has a {{date}} placeholder that can be replaced at runtime. For example, this can be used with the solidUpdateDocument command:

it('Watch a movie', () => {
    const now = new Date();

    cy.solidCreateDocument('/movies/movie', 'movie.ttl');
    cy.solidUpdateDocument('/movies/movie', 'watch-movie.sparql', {
        date: now.toISOString(),
    });
});

Replacements evaluation

The text within {{}} is actually treated as JavaScript and evaluated before rendering, so you can do things like {{ name || 'Fallback' }} in your fixture files.

Additionally, there are some helper functions you can use:

  • now() returns the current date in ISO string format.
  • date('2024-03-03') returns the parsed date in ISO string format.
  • uuid() returns a random UUID.

Here's an example of a complex fixture file used to test a SPARQL query:

INSERT DATA {
    @prefix schema: <https://schema.org/>.
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

    <#{{ resourceHash || uuid() }}>
        a schema:Movie ;
        schema:name {{ name || 'Spirited Away' }} ;
        schema:datePublished "{{ datePublished || date('2001-07-20') }}"^^xsd:dateTime .
}

Wildcards

Similarly, asserting some data that has been creating at runtime may be challenging. Wildcards can be used to run regular expression matches in assertions. They use the [[regex]] syntax.

For example, imagine that you want to assert that a Movie document has been updated properly:

it('Watch a movie', () => {
    cy.intercept('PATCH', podUrl('/movies/*')).as('watchMovie');

    // Interact with your application to watch a Movie...

    cy.get('@watchMovie')
        .its('request.body')
        .should(
            'be.sparql',
            `
                INSERT DATA {
                    @prefix schema: <https://schema.org/>.
                    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.

                    <#[[.*]]>
                        a schema:WatchAction ;
                        schema:object <#it> ;
                        schema:startTime "[[.*]]"^^xsd:dateTime .
                } .
            `
        );
});

In this case, we're using two wildcards. One for the value of schema:startTime, which is a date generated at runtime; and one for the fragment of the schema:WatchAction resource id, which could be a string generated dynamically.