- test
before_script:
- - npm install
+ - npm install
lint:
stage: lint
# Changelog
+
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
+
### Added
+
- Svelte based frontend
- Koa based backend
- RethinkDB for backend
- NPM tasks to lint and install git hooks
- A gitignore file
-[Unreleased]: https://gitlab.com/rbdr/forum/compare/master...develop
+[unreleased]: https://gitlab.com/rbdr/forum/compare/master...develop
and `jest`.
Here's some short guidelines:
- - Every component that receives props or changes state should be tested.
- - Test expected outcomes, not implementation details.
- - Avoid mocking unless it's an external call
- - Stores that interact with the API should be tested using pact
+
+- Every component that receives props or changes state should be tested.
+- Test expected outcomes, not implementation details.
+- Avoid mocking unless it's an external call
+- Stores that interact with the API should be tested using pact
[node]: https://nodejs.org/en/
[forum-server]: https://gitlab.com/rbdr/forum-server
-# Code Quality Report
-Thu Nov 15 2018 01:45:42 GMT+0100 (Central European Standard Time)
-
+# Code Quality Report
+
+Thu Nov 15 2018 01:45:42 GMT+0100 (Central European Standard Time)
+
## Tests
-
+
**Application Loader**
-✔ 1) Should instantiate and start Forum on window load (18 ms)
-
-
+✔ 1) Should instantiate and start Forum on window load (18 ms)
+
1 test
0 tests failed
-0 tests skipped
-
-Test duration: 27 ms
-
-
-## Leaks
-The following global variable leaks were detected:_registeredHandlers, _eventHandlers, DOMException, NamedNodeMap, Attr, Node, Element, DocumentFragment, HTMLDocument, Document, XMLDocument, CharacterData, Text, CDATASection, ProcessingInstruction, Comment, DocumentType, DOMImplementation, NodeList, HTMLCollection, HTMLOptionsCollection, DOMStringMap, DOMTokenList, SVGAnimatedString, SVGNumber, SVGStringList, Event, CloseEvent, CustomEvent, MessageEvent, ErrorEvent, HashChangeEvent, FocusEvent, PopStateEvent, UIEvent, MouseEvent, KeyboardEvent, TouchEvent, ProgressEvent, CompositionEvent, WheelEvent, EventTarget, BarProp, Location, History, Screen, Performance, Blob, File, FileList, DOMParser, FormData, XMLHttpRequestEventTarget, XMLHttpRequestUpload, NodeIterator, TreeWalker, HTMLElement, HTMLAnchorElement, HTMLAreaElement, HTMLAudioElement, HTMLBaseElement, HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDataElement, HTMLDataListElement, HTMLDetailsElement, HTMLDialogElement, HTMLDirectoryElement, HTMLDivElement, HTMLDListElement, HTMLEmbedElement, HTMLFieldSetElement, HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, HTMLHeadingElement, HTMLHeadElement, HTMLHRElement, HTMLHtmlElement, HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLLabelElement, HTMLLegendElement, HTMLLIElement, HTMLLinkElement, HTMLMapElement, HTMLMarqueeElement, HTMLMediaElement, HTMLMenuElement, HTMLMetaElement, HTMLMeterElement, HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, HTMLOptionElement, HTMLOutputElement, HTMLParagraphElement, HTMLParamElement, HTMLPictureElement, HTMLPreElement, HTMLProgressElement, HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLSourceElement, HTMLSpanElement, HTMLStyleElement, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement, HTMLTableElement, HTMLTimeElement, HTMLTitleElement, HTMLTableRowElement, HTMLTableSectionElement, HTMLTemplateElement, HTMLTextAreaElement, HTMLTrackElement, HTMLUListElement, HTMLUnknownElement, HTMLVideoElement, SVGElement, SVGGraphicsElement, SVGSVGElement, StyleSheet, MediaList, CSSStyleSheet, CSSRule, CSSStyleRule, CSSMediaRule, CSSImportRule, CSSStyleDeclaration, StyleSheetList, XPathException, XPathExpression, XPathResult, XPathEvaluator, NodeFilter, Window, _globalProxy, _document, _sessionHistory, _virtualConsole, _runScripts, _top, _parent, _frameElement, _length, _pretendToBeVisual, length, window, frameElement, frames, self, parent, top, document, external, location, history, navigator, locationbar, menubar, personalbar, scrollbars, statusbar, toolbar, performance, screen, addEventListener, removeEventListener, dispatchEvent, __stopAllTimers, Option, Image, Audio, postMessage, atob, btoa, FileReader, WebSocket, AbortSignal, AbortController, XMLHttpRequest, stop, close, getComputedStyle, captureEvents, releaseEvents, name, devicePixelRatio, innerWidth, innerHeight, outerWidth, outerHeight, pageXOffset, pageYOffset, screenX, screenY, scrollX, scrollY, screenLeft, screenTop, alert, blur, confirm, focus, moveBy, moveTo, open, print, prompt, resizeBy, resizeTo, scroll, scrollBy, scrollTo
-
-
-## Coverage
+0 tests skipped
+
+Test duration: 27 ms
+
+## Leaks
+
+The following global variable leaks were detected:\_registeredHandlers, \_eventHandlers, DOMException, NamedNodeMap, Attr, Node, Element, DocumentFragment, HTMLDocument, Document, XMLDocument, CharacterData, Text, CDATASection, ProcessingInstruction, Comment, DocumentType, DOMImplementation, NodeList, HTMLCollection, HTMLOptionsCollection, DOMStringMap, DOMTokenList, SVGAnimatedString, SVGNumber, SVGStringList, Event, CloseEvent, CustomEvent, MessageEvent, ErrorEvent, HashChangeEvent, FocusEvent, PopStateEvent, UIEvent, MouseEvent, KeyboardEvent, TouchEvent, ProgressEvent, CompositionEvent, WheelEvent, EventTarget, BarProp, Location, History, Screen, Performance, Blob, File, FileList, DOMParser, FormData, XMLHttpRequestEventTarget, XMLHttpRequestUpload, NodeIterator, TreeWalker, HTMLElement, HTMLAnchorElement, HTMLAreaElement, HTMLAudioElement, HTMLBaseElement, HTMLBodyElement, HTMLBRElement, HTMLButtonElement, HTMLCanvasElement, HTMLDataElement, HTMLDataListElement, HTMLDetailsElement, HTMLDialogElement, HTMLDirectoryElement, HTMLDivElement, HTMLDListElement, HTMLEmbedElement, HTMLFieldSetElement, HTMLFontElement, HTMLFormElement, HTMLFrameElement, HTMLFrameSetElement, HTMLHeadingElement, HTMLHeadElement, HTMLHRElement, HTMLHtmlElement, HTMLIFrameElement, HTMLImageElement, HTMLInputElement, HTMLLabelElement, HTMLLegendElement, HTMLLIElement, HTMLLinkElement, HTMLMapElement, HTMLMarqueeElement, HTMLMediaElement, HTMLMenuElement, HTMLMetaElement, HTMLMeterElement, HTMLModElement, HTMLObjectElement, HTMLOListElement, HTMLOptGroupElement, HTMLOptionElement, HTMLOutputElement, HTMLParagraphElement, HTMLParamElement, HTMLPictureElement, HTMLPreElement, HTMLProgressElement, HTMLQuoteElement, HTMLScriptElement, HTMLSelectElement, HTMLSourceElement, HTMLSpanElement, HTMLStyleElement, HTMLTableCaptionElement, HTMLTableCellElement, HTMLTableColElement, HTMLTableElement, HTMLTimeElement, HTMLTitleElement, HTMLTableRowElement, HTMLTableSectionElement, HTMLTemplateElement, HTMLTextAreaElement, HTMLTrackElement, HTMLUListElement, HTMLUnknownElement, HTMLVideoElement, SVGElement, SVGGraphicsElement, SVGSVGElement, StyleSheet, MediaList, CSSStyleSheet, CSSRule, CSSStyleRule, CSSMediaRule, CSSImportRule, CSSStyleDeclaration, StyleSheetList, XPathException, XPathExpression, XPathResult, XPathEvaluator, NodeFilter, Window, \_globalProxy, \_document, \_sessionHistory, \_virtualConsole, \_runScripts, \_top, \_parent, \_frameElement, \_length, \_pretendToBeVisual, length, window, frameElement, frames, self, parent, top, document, external, location, history, navigator, locationbar, menubar, personalbar, scrollbars, statusbar, toolbar, performance, screen, addEventListener, removeEventListener, dispatchEvent, \_\_stopAllTimers, Option, Image, Audio, postMessage, atob, btoa, FileReader, WebSocket, AbortSignal, AbortController, XMLHttpRequest, stop, close, getComputedStyle, captureEvents, releaseEvents, name, devicePixelRatio, innerWidth, innerHeight, outerWidth, outerHeight, pageXOffset, pageYOffset, screenX, screenY, scrollX, scrollY, screenLeft, screenTop, alert, blur, confirm, focus, moveBy, moveTo, open, print, prompt, resizeBy, resizeTo, scroll, scrollBy, scrollTo
+
+## Coverage
+
Threshold: 100%
-Coverage: 0.00% (0/0)
-
-
-
-## Linting
+Coverage: 0.00% (0/0)
+
+## Linting
+
Warnings threshold: 0
Errors threshold: 0
-No issues
-
+No issues
<a name="module_Forum"></a>
## Forum
-The Forum class is the main entry point for the backend application.
+The Forum class is the main entry point for the backend application.
-| Param | Type | Description |
-| --- | --- | --- |
+| Param | Type | Description |
+| ------ | ---------------------------------------------------------------------- | ------------------------------------------------- |
| config | [<code>tForumBackendConfiguration</code>](#tForumBackendConfiguration) | the initialization options to extend the instance |
<a name="module_Forum+run"></a>
### forum.run()
+
Initializes the application and starts listening. Also prints a
nice robotic banner with information.
<a name="tForumBackendConfiguration"></a>
## tForumBackendConfiguration : <code>object</code>
+
The main configuration object for the Forum backend. It will be used to
initialize all of the sub-components. It can extend any property of
the forum object.
**Kind**: global typedef
**Properties**
-| Name | Type | Default | Description |
-| --- | --- | --- | --- |
-| [port] | <code>number</code> | <code>1978</code> | the port where the app will listen on |
-| [staticDirectory] | <code>string</code> | <code>"static"</code> | the path, relative to the project root, where static assets live |
-| [ttl] | <code>number</code> | <code>180</code> | the time in seconds that posts remain alive |
-| rethinkDB | [<code>tRethinkDBConfiguration</code>](#tRethinkDBConfiguration) | | the configuration to connect to the rethinkDB server |
-| jwt | [<code>tJWTConfiguration</code>](#tJWTConfiguration) | | the configuration for the JWT authentication |
+| Name | Type | Default | Description |
+| ----------------- | ---------------------------------------------------------------- | ------------------------------- | ---------------------------------------------------------------- |
+| [port] | <code>number</code> | <code>1978</code> | the port where the app will listen on |
+| [staticDirectory] | <code>string</code> | <code>"static"</code> | the path, relative to the project root, where static assets live |
+| [ttl] | <code>number</code> | <code>180</code> | the time in seconds that posts remain alive |
+| rethinkDB | [<code>tRethinkDBConfiguration</code>](#tRethinkDBConfiguration) | | the configuration to connect to the rethinkDB server |
+| jwt | [<code>tJWTConfiguration</code>](#tJWTConfiguration) | | the configuration for the JWT authentication |
<a name="tJWTConfiguration"></a>
## tJWTConfiguration : <code>object</code>
+
Configures the behavior of the JWT token.
**Kind**: global typedef
**Properties**
-| Name | Type | Default | Description |
-| --- | --- | --- | --- |
+| Name | Type | Default | Description |
+| ---------- | ------------------- | ------------------ | ---------------------------------- |
| [duration] | <code>number</code> | <code>86400</code> | the duration of the JWT in seconds |
-| secret | <code>string</code> | | the secret used to sign the JWT |
+| secret | <code>string</code> | | the secret used to sign the JWT |
<a name="tRethinkDBConfiguration"></a>
## tRethinkDBConfiguration : <code>object</code>
+
Information required to connect to the rethinkDB server
**Kind**: global typedef
**Properties**
-| Name | Type | Default | Description |
-| --- | --- | --- | --- |
-| host | <code>string</code> | | the location of the rethinkDB host |
+| Name | Type | Default | Description |
+| ------ | ------------------- | ----------------- | ---------------------------------------- |
+| host | <code>string</code> | | the location of the rethinkDB host |
| [post] | <code>string</code> | <code>6379</code> | port where rethinkDB server is listening |
-
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
export default {
- preset: "ts-jest",
- transform: {
- "^.+\\.svelte$": [
- "svelte-jester",
- {
- "preprocess": true
- }
- ]
- },
- moduleNameMapper: {
- "^\\$lib(.*)$": "<rootDir>/src/lib$1",
- "^\\$app(.*)$": [".svelte/dev/runtime/app/*", ".svelte/build/runtime/app/*"]
- },
- moduleFileExtensions: ["ts", "js", "svelte"],
- globals: {
- 'ts-jest': {
- diagnostics: {
- ignoreCodes: [ 'TS151001' ],
- },
- },
- },
-}
+ preset: 'ts-jest',
+ transform: {
+ '^.+\\.svelte$': [
+ 'svelte-jester',
+ {
+ preprocess: true
+ }
+ ]
+ },
+ moduleNameMapper: {
+ '^\\$lib(.*)$': '<rootDir>/src/lib$1',
+ '^\\$app(.*)$': ['.svelte/dev/runtime/app/*', '.svelte/build/runtime/app/*']
+ },
+ moduleFileExtensions: ['ts', 'js', 'svelte'],
+ globals: {
+ 'ts-jest': {
+ diagnostics: {
+ ignoreCodes: ['TS151001']
+ }
+ }
+ }
+};
{
- "consumer": {
- "name": "ForumClient"
- },
- "provider": {
- "name": "ForumServer"
- },
- "interactions": [
- {
- "description": "a request to list the forums",
- "providerState": "there's data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForums",
- "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
- "variables": {
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "forums": [
- {
- "id": "butter",
- "glyph": "⌘",
- "label": "test_forums.butter",
- "position": 1
- }
- ]
- }
- },
- "matchingRules": {
- "$.body.data.forums": {
- "min": 1
- },
- "$.body.data.forums[*].*": {
- "match": "type"
- },
- "$.body.data.forums[*].id": {
- "match": "type"
- },
- "$.body.data.forums[*].glyph": {
- "match": "type"
- },
- "$.body.data.forums[*].label": {
- "match": "type"
- },
- "$.body.data.forums[*].position": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single forum",
- "providerState": "there's data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForum",
- "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "freezer"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "forum": {
- "id": "freezer",
- "glyph": "✭",
- "label": "test_forums.freezer",
- "position": 3,
- "topics": [
- {
- "id": "629de02c-151a-4db7-bb86-43b2add8a15a",
- "title": "Very pacty topic",
- "updated_at": 1619954611616,
- "ttl": 3601
- }
- ]
- }
- }
- },
- "matchingRules": {
- "$.body.data.forum": {
- "match": "type"
- },
- "$.body.data.forum.glyph": {
- "match": "type"
- },
- "$.body.data.forum.label": {
- "match": "type"
- },
- "$.body.data.forum.position": {
- "match": "type"
- },
- "$.body.data.forum.topics": {
- "min": 1
- },
- "$.body.data.forum.topics[*].*": {
- "match": "type"
- },
- "$.body.data.forum.topics[*].id": {
- "match": "type"
- },
- "$.body.data.forum.topics[*].title": {
- "match": "type"
- },
- "$.body.data.forum.topics[*].updated_at": {
- "match": "type"
- },
- "$.body.data.forum.topics[*].ttl": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to list the forums",
- "providerState": "there's no data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForums",
- "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
- "variables": {
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "forums": [
-
- ]
- }
- }
- }
- },
- {
- "description": "a request to get a single forum",
- "providerState": "there's no data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForum",
- "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "freezer"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "forum": null
- }
- }
- }
- },
- {
- "description": "a request to list the forums",
- "providerState": "there's a server error",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForums",
- "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
- "variables": {
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 500,
- "headers": {
- }
- }
- },
- {
- "description": "a request to get a single forum",
- "providerState": "there's a server error",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForum",
- "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "freezer"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 500,
- "headers": {
- }
- }
- },
- {
- "description": "a request to list the forums",
- "providerState": "there's an error in the response",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForums",
- "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
- "variables": {
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "errors": [
- {
- "message": "An error occurred when fetching forums"
- }
- ]
- },
- "matchingRules": {
- "$.body.errors": {
- "min": 1
- },
- "$.body.errors[*].*": {
- "match": "type"
- },
- "$.body.errors[*].message": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single forum",
- "providerState": "there's an error in the response",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetForum",
- "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "freezer"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "errors": [
- {
- "message": "An error occurred when fetching the forum"
- }
- ]
- },
- "matchingRules": {
- "$.body.errors": {
- "min": 1
- },
- "$.body.errors[*].*": {
- "match": "type"
- },
- "$.body.errors[*].message": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single topic",
- "providerState": "there's data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTopic",
- "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "topic": {
- "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1",
- "title": "The pacty topic of the day",
- "updated_at": 1619979888906,
- "ttl": 3399,
- "forum": {
- "id": "cucumber",
- "glyph": "✽",
- "label": "test_forums.cucumber"
- },
- "tags": [
- {
- "id": "skunk",
- "weight": 44
- }
- ],
- "posts": [
- {
- "id": "ed93530e-6f9c-4701-91ef-14f9e0ed3e26",
- "text": "The content of this post is very relevant",
- "created_at": 1619979889798,
- "author": {
- "id": "07fb2ba0-0945-464a-b215-873296710c8c",
- "handle": "cucumber_fan92"
- }
- }
- ]
- }
- }
- },
- "matchingRules": {
- "$.body.data.topic.id": {
- "match": "type"
- },
- "$.body.data.topic.title": {
- "match": "type"
- },
- "$.body.data.topic.updated_at": {
- "match": "type"
- },
- "$.body.data.topic.ttl": {
- "match": "type"
- },
- "$.body.data.topic.forum.id": {
- "match": "type"
- },
- "$.body.data.topic.forum.glyph": {
- "match": "type"
- },
- "$.body.data.topic.forum.label": {
- "match": "type"
- },
- "$.body.data.topic.tags": {
- "min": 1
- },
- "$.body.data.topic.tags[*].*": {
- "match": "type"
- },
- "$.body.data.topic.tags[*].id": {
- "match": "type"
- },
- "$.body.data.topic.tags[*].weight": {
- "match": "type"
- },
- "$.body.data.topic.posts": {
- "min": 1
- },
- "$.body.data.topic.posts[*].*": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].id": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].text": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].created_at": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].author": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].author.id": {
- "match": "type"
- },
- "$.body.data.topic.posts[*].author.handle": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single topic",
- "providerState": "there's no data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTopic",
- "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "topic": null
- }
- }
- }
- },
- {
- "description": "a request to get a single topic",
- "providerState": "there's a server error",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTopic",
- "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 500,
- "headers": {
- }
- }
- },
- {
- "description": "a request to get a single topic",
- "providerState": "there's an error in the response",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTopic",
- "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "errors": [
- {
- "message": "An error occurred when fetching the topic"
- }
- ]
- },
- "matchingRules": {
- "$.body.errors": {
- "min": 1
- },
- "$.body.errors[*].*": {
- "match": "type"
- },
- "$.body.errors[*].message": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single tag",
- "providerState": "there's data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTag",
- "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "pineapple"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "tag": {
- "id": "pineapple",
- "topics": [
- {
- "id": "cd038ae7-e8b4-4e38-9543-3d697e69ac34",
- "title": "This topic is about pineapples",
- "updated_at": 1619978944077,
- "ttl": 3555
- }
- ]
- }
- }
- },
- "matchingRules": {
- "$.body.data.tag.id": {
- "match": "type"
- },
- "$.body.data.tag.topics": {
- "min": 1
- },
- "$.body.data.tag.topics[*].*": {
- "match": "type"
- },
- "$.body.data.tag.topics[*].id": {
- "match": "type"
- },
- "$.body.data.tag.topics[*].title": {
- "match": "type"
- },
- "$.body.data.tag.topics[*].updated_at": {
- "match": "type"
- },
- "$.body.data.tag.topics[*].ttl": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single tag",
- "providerState": "there's no data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTag",
- "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "pineapple"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "tag": null
- }
- }
- }
- },
- {
- "description": "a request to get a single tag",
- "providerState": "there's a server error",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTag",
- "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "pineapple"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 500,
- "headers": {
- }
- }
- },
- {
- "description": "a request to get a single tag",
- "providerState": "there's an error in the response",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetTag",
- "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "pineapple"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "errors": [
- {
- "message": "An error occurred when fetching the tag"
- }
- ]
- },
- "matchingRules": {
- "$.body.errors": {
- "min": 1
- },
- "$.body.errors[*].*": {
- "match": "type"
- },
- "$.body.errors[*].message": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single post",
- "providerState": "there's data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetPost",
- "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "post": {
- "id": "8f75eba5-6989-4dd3-b466-e464546ce374",
- "text": "This is a very pacty post",
- "created_at": 1619976194937,
- "author": {
- "id": "a805b3de-cac4-451c-a1e6-f078869c9db9",
- "handle": "pacts_person"
- },
- "topic": {
- "id": "5c283ce1-0470-4b98-86f5-1fec9a22c9ac",
- "title": "The parent pacts topic"
- }
- }
- }
- },
- "matchingRules": {
- "$.body.data.post": {
- "match": "type"
- },
- "$.body.data.post.id": {
- "match": "type"
- },
- "$.body.data.post.text": {
- "match": "type"
- },
- "$.body.data.post.created_at": {
- "match": "type"
- },
- "$.body.data.post.author": {
- "match": "type"
- },
- "$.body.data.post.author.id": {
- "match": "type"
- },
- "$.body.data.post.author.handle": {
- "match": "type"
- },
- "$.body.data.post.topic": {
- "match": "type"
- },
- "$.body.data.post.topic.id": {
- "match": "type"
- },
- "$.body.data.post.topic.title": {
- "match": "type"
- }
- }
- }
- },
- {
- "description": "a request to get a single post",
- "providerState": "there's no data",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetPost",
- "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "data": {
- "post": null
- }
- }
- }
- },
- {
- "description": "a request to get a single post",
- "providerState": "there's a server error",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetPost",
- "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 500,
- "headers": {
- }
- }
- },
- {
- "description": "a request to get a single post",
- "providerState": "there's an error in the response",
- "request": {
- "method": "POST",
- "path": "/graphql",
- "headers": {
- "content-type": "application/json"
- },
- "body": {
- "operationName": "GetPost",
- "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
- "variables": {
- "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
- }
- },
- "matchingRules": {
- "$.body.query": {
- "match": "regex",
- "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
- }
- }
- },
- "response": {
- "status": 200,
- "headers": {
- "Content-Type": "application/json; charset=utf-8"
- },
- "body": {
- "errors": [
- {
- "message": "An error occurred when fetching the post"
- }
- ]
- },
- "matchingRules": {
- "$.body.errors": {
- "min": 1
- },
- "$.body.errors[*].*": {
- "match": "type"
- },
- "$.body.errors[*].message": {
- "match": "type"
- }
- }
- }
- }
- ],
- "metadata": {
- "pactSpecification": {
- "version": "2.0.0"
- }
- }
-}
\ No newline at end of file
+ "consumer": {
+ "name": "ForumClient"
+ },
+ "provider": {
+ "name": "ForumServer"
+ },
+ "interactions": [
+ {
+ "description": "a request to list the forums",
+ "providerState": "there's data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForums",
+ "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
+ "variables": {}
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "forums": [
+ {
+ "id": "butter",
+ "glyph": "⌘",
+ "label": "test_forums.butter",
+ "position": 1
+ }
+ ]
+ }
+ },
+ "matchingRules": {
+ "$.body.data.forums": {
+ "min": 1
+ },
+ "$.body.data.forums[*].*": {
+ "match": "type"
+ },
+ "$.body.data.forums[*].id": {
+ "match": "type"
+ },
+ "$.body.data.forums[*].glyph": {
+ "match": "type"
+ },
+ "$.body.data.forums[*].label": {
+ "match": "type"
+ },
+ "$.body.data.forums[*].position": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single forum",
+ "providerState": "there's data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForum",
+ "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "freezer"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "forum": {
+ "id": "freezer",
+ "glyph": "✭",
+ "label": "test_forums.freezer",
+ "position": 3,
+ "topics": [
+ {
+ "id": "629de02c-151a-4db7-bb86-43b2add8a15a",
+ "title": "Very pacty topic",
+ "updated_at": 1619954611616,
+ "ttl": 3601
+ }
+ ]
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body.data.forum": {
+ "match": "type"
+ },
+ "$.body.data.forum.glyph": {
+ "match": "type"
+ },
+ "$.body.data.forum.label": {
+ "match": "type"
+ },
+ "$.body.data.forum.position": {
+ "match": "type"
+ },
+ "$.body.data.forum.topics": {
+ "min": 1
+ },
+ "$.body.data.forum.topics[*].*": {
+ "match": "type"
+ },
+ "$.body.data.forum.topics[*].id": {
+ "match": "type"
+ },
+ "$.body.data.forum.topics[*].title": {
+ "match": "type"
+ },
+ "$.body.data.forum.topics[*].updated_at": {
+ "match": "type"
+ },
+ "$.body.data.forum.topics[*].ttl": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to list the forums",
+ "providerState": "there's no data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForums",
+ "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
+ "variables": {}
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "forums": []
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single forum",
+ "providerState": "there's no data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForum",
+ "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "freezer"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "forum": null
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to list the forums",
+ "providerState": "there's a server error",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForums",
+ "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
+ "variables": {}
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 500,
+ "headers": {}
+ }
+ },
+ {
+ "description": "a request to get a single forum",
+ "providerState": "there's a server error",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForum",
+ "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "freezer"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 500,
+ "headers": {}
+ }
+ },
+ {
+ "description": "a request to list the forums",
+ "providerState": "there's an error in the response",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForums",
+ "query": "query GetForums {\n forums {\n id\n glyph\n label\n position\n __typename\n }\n }",
+ "variables": {}
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForums\\s*\\{\\s*forums\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "errors": [
+ {
+ "message": "An error occurred when fetching forums"
+ }
+ ]
+ },
+ "matchingRules": {
+ "$.body.errors": {
+ "min": 1
+ },
+ "$.body.errors[*].*": {
+ "match": "type"
+ },
+ "$.body.errors[*].message": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single forum",
+ "providerState": "there's an error in the response",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetForum",
+ "query": "query GetForum($id: ID!) {\n forum(id: $id) {\n id\n glyph\n label\n position\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "freezer"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetForum\\(\\$id:\\s*ID!\\)\\s*\\{\\s*forum\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*glyph\\s*label\\s*position\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "errors": [
+ {
+ "message": "An error occurred when fetching the forum"
+ }
+ ]
+ },
+ "matchingRules": {
+ "$.body.errors": {
+ "min": 1
+ },
+ "$.body.errors[*].*": {
+ "match": "type"
+ },
+ "$.body.errors[*].message": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single topic",
+ "providerState": "there's data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTopic",
+ "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "topic": {
+ "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1",
+ "title": "The pacty topic of the day",
+ "updated_at": 1619979888906,
+ "ttl": 3399,
+ "forum": {
+ "id": "cucumber",
+ "glyph": "✽",
+ "label": "test_forums.cucumber"
+ },
+ "tags": [
+ {
+ "id": "skunk",
+ "weight": 44
+ }
+ ],
+ "posts": [
+ {
+ "id": "ed93530e-6f9c-4701-91ef-14f9e0ed3e26",
+ "text": "The content of this post is very relevant",
+ "created_at": 1619979889798,
+ "author": {
+ "id": "07fb2ba0-0945-464a-b215-873296710c8c",
+ "handle": "cucumber_fan92"
+ }
+ }
+ ]
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body.data.topic.id": {
+ "match": "type"
+ },
+ "$.body.data.topic.title": {
+ "match": "type"
+ },
+ "$.body.data.topic.updated_at": {
+ "match": "type"
+ },
+ "$.body.data.topic.ttl": {
+ "match": "type"
+ },
+ "$.body.data.topic.forum.id": {
+ "match": "type"
+ },
+ "$.body.data.topic.forum.glyph": {
+ "match": "type"
+ },
+ "$.body.data.topic.forum.label": {
+ "match": "type"
+ },
+ "$.body.data.topic.tags": {
+ "min": 1
+ },
+ "$.body.data.topic.tags[*].*": {
+ "match": "type"
+ },
+ "$.body.data.topic.tags[*].id": {
+ "match": "type"
+ },
+ "$.body.data.topic.tags[*].weight": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts": {
+ "min": 1
+ },
+ "$.body.data.topic.posts[*].*": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].id": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].text": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].created_at": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].author": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].author.id": {
+ "match": "type"
+ },
+ "$.body.data.topic.posts[*].author.handle": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single topic",
+ "providerState": "there's no data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTopic",
+ "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "topic": null
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single topic",
+ "providerState": "there's a server error",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTopic",
+ "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 500,
+ "headers": {}
+ }
+ },
+ {
+ "description": "a request to get a single topic",
+ "providerState": "there's an error in the response",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTopic",
+ "query": "query GetTopic($id: ID!) {\n topic(id: $id) {\n id\n title\n updated_at\n ttl\n forum {\n id\n glyph\n label\n __typename\n }\n tags {\n id\n weight\n __typename\n }\n posts {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "0b58959d-d448-4a4e-84b6-35e5ac0028d1"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTopic\\(\\$id:\\s*ID!\\)\\s*\\{\\s*topic\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*forum\\s*\\{\\s*id\\s*glyph\\s*label\\s*__typename\\s*\\}\\s*tags\\s*\\{\\s*id\\s*weight\\s*__typename\\s*\\}\\s*posts\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "errors": [
+ {
+ "message": "An error occurred when fetching the topic"
+ }
+ ]
+ },
+ "matchingRules": {
+ "$.body.errors": {
+ "min": 1
+ },
+ "$.body.errors[*].*": {
+ "match": "type"
+ },
+ "$.body.errors[*].message": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single tag",
+ "providerState": "there's data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTag",
+ "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "pineapple"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "tag": {
+ "id": "pineapple",
+ "topics": [
+ {
+ "id": "cd038ae7-e8b4-4e38-9543-3d697e69ac34",
+ "title": "This topic is about pineapples",
+ "updated_at": 1619978944077,
+ "ttl": 3555
+ }
+ ]
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body.data.tag.id": {
+ "match": "type"
+ },
+ "$.body.data.tag.topics": {
+ "min": 1
+ },
+ "$.body.data.tag.topics[*].*": {
+ "match": "type"
+ },
+ "$.body.data.tag.topics[*].id": {
+ "match": "type"
+ },
+ "$.body.data.tag.topics[*].title": {
+ "match": "type"
+ },
+ "$.body.data.tag.topics[*].updated_at": {
+ "match": "type"
+ },
+ "$.body.data.tag.topics[*].ttl": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single tag",
+ "providerState": "there's no data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTag",
+ "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "pineapple"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "tag": null
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single tag",
+ "providerState": "there's a server error",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTag",
+ "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "pineapple"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 500,
+ "headers": {}
+ }
+ },
+ {
+ "description": "a request to get a single tag",
+ "providerState": "there's an error in the response",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetTag",
+ "query": "query GetTag($id: ID!) {\n tag(id: $id) {\n id\n topics {\n id\n title\n updated_at\n ttl\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "pineapple"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetTag\\(\\$id:\\s*ID!\\)\\s*\\{\\s*tag\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*topics\\s*\\{\\s*id\\s*title\\s*updated_at\\s*ttl\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "errors": [
+ {
+ "message": "An error occurred when fetching the tag"
+ }
+ ]
+ },
+ "matchingRules": {
+ "$.body.errors": {
+ "min": 1
+ },
+ "$.body.errors[*].*": {
+ "match": "type"
+ },
+ "$.body.errors[*].message": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single post",
+ "providerState": "there's data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetPost",
+ "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "post": {
+ "id": "8f75eba5-6989-4dd3-b466-e464546ce374",
+ "text": "This is a very pacty post",
+ "created_at": 1619976194937,
+ "author": {
+ "id": "a805b3de-cac4-451c-a1e6-f078869c9db9",
+ "handle": "pacts_person"
+ },
+ "topic": {
+ "id": "5c283ce1-0470-4b98-86f5-1fec9a22c9ac",
+ "title": "The parent pacts topic"
+ }
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body.data.post": {
+ "match": "type"
+ },
+ "$.body.data.post.id": {
+ "match": "type"
+ },
+ "$.body.data.post.text": {
+ "match": "type"
+ },
+ "$.body.data.post.created_at": {
+ "match": "type"
+ },
+ "$.body.data.post.author": {
+ "match": "type"
+ },
+ "$.body.data.post.author.id": {
+ "match": "type"
+ },
+ "$.body.data.post.author.handle": {
+ "match": "type"
+ },
+ "$.body.data.post.topic": {
+ "match": "type"
+ },
+ "$.body.data.post.topic.id": {
+ "match": "type"
+ },
+ "$.body.data.post.topic.title": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single post",
+ "providerState": "there's no data",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetPost",
+ "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "data": {
+ "post": null
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to get a single post",
+ "providerState": "there's a server error",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetPost",
+ "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 500,
+ "headers": {}
+ }
+ },
+ {
+ "description": "a request to get a single post",
+ "providerState": "there's an error in the response",
+ "request": {
+ "method": "POST",
+ "path": "/graphql",
+ "headers": {
+ "content-type": "application/json"
+ },
+ "body": {
+ "operationName": "GetPost",
+ "query": "query GetPost($id: ID!) {\n post(id: $id) {\n id\n text\n created_at\n author {\n id\n handle\n __typename\n }\n topic {\n id\n title\n __typename\n }\n __typename\n }\n }",
+ "variables": {
+ "id": "8f75eba5-6989-4dd3-b466-e464546ce374"
+ }
+ },
+ "matchingRules": {
+ "$.body.query": {
+ "match": "regex",
+ "regex": "query\\s*GetPost\\(\\$id:\\s*ID!\\)\\s*\\{\\s*post\\(id:\\s*\\$id\\)\\s*\\{\\s*id\\s*text\\s*created_at\\s*author\\s*\\{\\s*id\\s*handle\\s*__typename\\s*\\}\\s*topic\\s*\\{\\s*id\\s*title\\s*__typename\\s*\\}\\s*__typename\\s*\\}\\s*\\}"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/json; charset=utf-8"
+ },
+ "body": {
+ "errors": [
+ {
+ "message": "An error occurred when fetching the post"
+ }
+ ]
+ },
+ "matchingRules": {
+ "$.body.errors": {
+ "min": 1
+ },
+ "$.body.errors[*].*": {
+ "match": "type"
+ },
+ "$.body.errors[*].message": {
+ "match": "type"
+ }
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "pactSpecification": {
+ "version": "2.0.0"
+ }
+ }
+}
module.exports = {
- plugins: {
- tailwindcss: {},
- autoprefixer: {},
- },
-}
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {}
+ }
+};
import { sineOut } from 'svelte/easing';
const internals = {
- response: null
+ response: null
};
describe('blink', () => {
-
- test('it has a default delay of 0ms', () => {
-
- const response = blink(document.createElement('div'), {});
-
- expect(response.delay).toBe(0);
- });
-
- test('it allows delay to be overridden', () => {
-
- const response = blink(document.createElement('div'), {
- delay: 300
- });
-
- expect(response.delay).toBe(300);
- });
-
- test('it has a default duration of 400ms', () => {
-
- const response = blink(document.createElement('div'), {});
-
- expect(response.duration).toBe(400);
- });
-
- test('it allows delay to be overridden', () => {
-
- const response = blink(document.createElement('div'), {
- duration: 999
- });
-
- expect(response.duration).toBe(999);
- });
-
- test('it uses sineOut as the default easing function', () => {
-
- const response = blink(document.createElement('div'), {});
-
- expect(response.easing).toBe(sineOut);
- });
-
- test('it allows easing function to be overridden', () => {
-
- const response = blink(document.createElement('div'), {
- easing: () => 666
- });
-
- expect(response.easing(0)).toBe(666);
- });
-
- describe('css animation function', () => {
-
- beforeEach(() => {
-
- const div = document.createElement('div');
- div.style.width = '100px';
- div.style.height = '200px';
- internals.response = blink(div, {});
- });
-
- test('It starts with with zeroed width and height', () => {
-
- const css = internals.response.css(0, 1);
- expect(css).toContain('width: 0px');
- expect(css).toContain('height: 0px');
- });
-
- test('It grows to full height and 0 width in first 20%', () => {
-
- const css = internals.response.css(0.2, 0.8);
- expect(css).toContain('width: 0px');
- expect(css).toContain('height: 200px');
- });
-
- test('It expands to full height by the end', () => {
-
- const css = internals.response.css(1, 0);
- expect(css).toContain('width: 100px');
- expect(css).toContain('height: 200px');
- });
-
- test('It keeps element vertically centered by adjusting the margin', () => {
-
- const css = internals.response.css(0.1, 0.9);
- expect(css).toContain('margin: 50px 50px');
- expect(css).toContain('height: 100px');
- });
-
- test('It keeps element horizontally centered by adjusting the margin', () => {
-
- const css = internals.response.css(0.6, 0.4);
- expect(css).toContain('margin: 0px 25px');
- expect(css).toContain('width: 50px');
- });
- });
+ test('it has a default delay of 0ms', () => {
+ const response = blink(document.createElement('div'), {});
+
+ expect(response.delay).toBe(0);
+ });
+
+ test('it allows delay to be overridden', () => {
+ const response = blink(document.createElement('div'), {
+ delay: 300
+ });
+
+ expect(response.delay).toBe(300);
+ });
+
+ test('it has a default duration of 400ms', () => {
+ const response = blink(document.createElement('div'), {});
+
+ expect(response.duration).toBe(400);
+ });
+
+ test('it allows delay to be overridden', () => {
+ const response = blink(document.createElement('div'), {
+ duration: 999
+ });
+
+ expect(response.duration).toBe(999);
+ });
+
+ test('it uses sineOut as the default easing function', () => {
+ const response = blink(document.createElement('div'), {});
+
+ expect(response.easing).toBe(sineOut);
+ });
+
+ test('it allows easing function to be overridden', () => {
+ const response = blink(document.createElement('div'), {
+ easing: () => 666
+ });
+
+ expect(response.easing(0)).toBe(666);
+ });
+
+ describe('css animation function', () => {
+ beforeEach(() => {
+ const div = document.createElement('div');
+ div.style.width = '100px';
+ div.style.height = '200px';
+ internals.response = blink(div, {});
+ });
+
+ test('It starts with with zeroed width and height', () => {
+ const css = internals.response.css(0, 1);
+ expect(css).toContain('width: 0px');
+ expect(css).toContain('height: 0px');
+ });
+
+ test('It grows to full height and 0 width in first 20%', () => {
+ const css = internals.response.css(0.2, 0.8);
+ expect(css).toContain('width: 0px');
+ expect(css).toContain('height: 200px');
+ });
+
+ test('It expands to full height by the end', () => {
+ const css = internals.response.css(1, 0);
+ expect(css).toContain('width: 100px');
+ expect(css).toContain('height: 200px');
+ });
+
+ test('It keeps element vertically centered by adjusting the margin', () => {
+ const css = internals.response.css(0.1, 0.9);
+ expect(css).toContain('margin: 50px 50px');
+ expect(css).toContain('height: 100px');
+ });
+
+ test('It keeps element horizontally centered by adjusting the margin', () => {
+ const css = internals.response.css(0.6, 0.4);
+ expect(css).toContain('margin: 0px 25px');
+ expect(css).toContain('width: 50px');
+ });
+ });
});
import { sineOut } from 'svelte/easing';
import type { AnimationConfig } from 'svelte/animate';
-export const blink = function blink(node: HTMLElement, params: AnimationConfig): AnimationConfig{
+export const blink = function blink(node: HTMLElement, params: AnimationConfig): AnimationConfig {
+ const originalWidth = parseFloat(getComputedStyle(node).width);
+ const originalHeight = parseFloat(getComputedStyle(node).height);
- const originalWidth = parseFloat(getComputedStyle(node).width);
- const originalHeight = parseFloat(getComputedStyle(node).height);
+ return {
+ delay: params.delay || 0,
+ duration: params.duration || 400,
+ easing: params.easing || sineOut,
+ css: (t: number): string => {
+ const halfWidth = originalWidth / 2;
+ const halfHeight = originalHeight / 2;
+ const height = Math.round(t <= 0.2 ? (originalHeight * t) / 0.2 : originalHeight);
+ const marginY = Math.round(t <= 0.2 ? halfHeight * (1 - t / 0.2) : 0);
+ const width = Math.round(t > 0.2 ? ((t - 0.2) / 0.8) * originalWidth : 0);
+ const marginX = Math.round(t > 0.2 ? (1 - (t - 0.2) / 0.8) * halfWidth : halfWidth);
- return {
- delay: params.delay || 0,
- duration: params.duration || 400,
- easing: params.easing || sineOut,
- css: (t: number): string => {
-
- const halfWidth = originalWidth / 2;
- const halfHeight = originalHeight / 2;
- const height = Math.round(t <= 0.2 ? (originalHeight * t) / 0.2 : originalHeight);
- const marginY = Math.round(t <= 0.2 ? halfHeight * (1 - t / 0.2) : 0);
- const width = Math.round(t > 0.2 ? ((t - 0.2) / 0.8) * originalWidth : 0);
- const marginX = Math.round(t > 0.2 ? (1 - (t - 0.2) / 0.8) * halfWidth : halfWidth);
-
- return `width: ${width}px; height: ${height}px; margin: ${marginY}px ${marginX}px`;
- }
- };
+ return `width: ${width}px; height: ${height}px; margin: ${marginY}px ${marginX}px`;
+ }
+ };
};
<script lang="ts">
- import type { TopicAction } from '$lib/stores/action';
+ import type { TopicAction } from '$lib/stores/action';
export let actions: TopicAction;
import { _ } from 'svelte-i18n';
</script>
<li>
- <a href="/reply/{actions.id}" title={$_('header.action.reply.title')}>
- {@html $_('header.action.reply.display')}
- </a>
+ <a href="/reply/{actions.id}" title={$_('header.action.reply.title')}>
+ {@html $_('header.action.reply.display')}
+ </a>
</li>
<style>
import Topic from './topic.svelte';
-const internals = {
- results: null
-};
-
describe('Topic Actions component', () => {
-
- test('Should link to reply page', () => {
-
- const results = render(Topic, { props: {
- actions: {
- id: '8ebaa211-fd9b-423a-8b4f-b57622007fde'
- }
- } });
-
- expect(results.getByTitle('Reply').closest('a'))
- .toHaveAttribute('href', '/reply/8ebaa211-fd9b-423a-8b4f-b57622007fde');
- });
+ test('Should link to reply page', () => {
+ const results = render(Topic, {
+ props: {
+ actions: {
+ id: '8ebaa211-fd9b-423a-8b4f-b57622007fde'
+ }
+ }
+ });
+
+ expect(results.getByTitle('Reply').closest('a')).toHaveAttribute(
+ 'href',
+ '/reply/8ebaa211-fd9b-423a-8b4f-b57622007fde'
+ );
+ });
});
import ErrorBlock from './error_block.svelte';
describe('Error Block component', () => {
+ test('Should display error message sent', () => {
+ const results = render(ErrorBlock, {
+ props: {
+ message: 'An error has, sadly, destroyed everything.'
+ }
+ });
- test('Should display error message sent', () => {
+ expect(results.getByText('An error has, sadly, destroyed everything.')).toBeVisible();
+ });
- const results = render(ErrorBlock, { props: {
- message: 'An error has, sadly, destroyed everything.'
- } });
+ test('Should display default error message', () => {
+ const results = render(ErrorBlock);
- expect(results.getByText('An error has, sadly, destroyed everything.'))
- .toBeVisible();
- });
-
- test('Should display default error message', () => {
-
- const results = render(ErrorBlock);
-
- expect(results.getByText('Unknown error has occurred. Panic!'))
- .toBeVisible();
- });
+ expect(results.getByText('Unknown error has occurred. Panic!')).toBeVisible();
+ });
});
-
import Forum from './forum.svelte';
const internals = {
- results: null
+ results: null
};
describe('Forum component', () => {
-
- beforeAll(() => {
-
- addMessages('en', {
- 'test_forums.oleo': 'Oleo'
- });
- });
-
- beforeEach(() => {
-
- internals.results = render(Forum, { props: {
- forum: {
- id: 'oleo',
- glyph: '☽',
- label: 'test_forums.oleo',
- topics: [
- {
- id: '0575d375-5bea-44df-a597-bee3adda624d',
- title: 'Very forumy topic',
- ttl: 160 * 1000,
- updated_at: Date.now()
- },
- {
- id: 'aeeb56e4-751d-4400-8aa7-d0f3a20d4e25',
- title: 'Only mildly forum-like',
- ttl: 160 * 1000,
- updated_at: Date.now()
- }
- ]
- }
- } });
- });
-
- test('It should display the forum glyph and label', () => {
-
- expect(internals.results.getByText(/^\s*☽\s*Oleo\s*$/))
- .toBeVisible();
- });
-
- test('It should display the topics', () => {
-
- expect(internals.results.getByText('Very forumy topic'))
- .toBeVisible();
- expect(internals.results.getByText('Only mildly forum-like'))
- .toBeVisible();
- });
-
- test('It should link to the topics', () => {
-
- expect(internals.results.getByText('Very forumy topic').closest('a'))
- .toHaveAttribute('href', '/t/0575d375-5bea-44df-a597-bee3adda624d');
- expect(internals.results.getByText('Only mildly forum-like').closest('a'))
- .toHaveAttribute('href', '/t/aeeb56e4-751d-4400-8aa7-d0f3a20d4e25');
- });
+ beforeAll(() => {
+ addMessages('en', {
+ 'test_forums.oleo': 'Oleo'
+ });
+ });
+
+ beforeEach(() => {
+ internals.results = render(Forum, {
+ props: {
+ forum: {
+ id: 'oleo',
+ glyph: '☽',
+ label: 'test_forums.oleo',
+ topics: [
+ {
+ id: '0575d375-5bea-44df-a597-bee3adda624d',
+ title: 'Very forumy topic',
+ ttl: 160 * 1000,
+ updated_at: Date.now()
+ },
+ {
+ id: 'aeeb56e4-751d-4400-8aa7-d0f3a20d4e25',
+ title: 'Only mildly forum-like',
+ ttl: 160 * 1000,
+ updated_at: Date.now()
+ }
+ ]
+ }
+ }
+ });
+ });
+
+ test('It should display the forum glyph and label', () => {
+ expect(internals.results.getByText(/^\s*☽\s*Oleo\s*$/)).toBeVisible();
+ });
+
+ test('It should display the topics', () => {
+ expect(internals.results.getByText('Very forumy topic')).toBeVisible();
+ expect(internals.results.getByText('Only mildly forum-like')).toBeVisible();
+ });
+
+ test('It should link to the topics', () => {
+ expect(internals.results.getByText('Very forumy topic').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/0575d375-5bea-44df-a597-bee3adda624d'
+ );
+ expect(internals.results.getByText('Only mildly forum-like').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/aeeb56e4-751d-4400-8aa7-d0f3a20d4e25'
+ );
+ });
});
<script lang="ts">
- import { _ } from 'svelte-i18n';
- export let forums;
+ import { _ } from 'svelte-i18n';
+ export let forums;
- $: sortedForums = forums.slice().sort((a, b) => a.position - b.position);
+ $: sortedForums = forums.slice().sort((a, b) => a.position - b.position);
</script>
<ul>
- {#each sortedForums as forum}
- <li>
- <a href="/f/{forum.id}">
- <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
- <span class="navigation-label">{$_(forum.label)}</span>
- </a>
- </li>
- {/each}
+ {#each sortedForums as forum}
+ <li>
+ <a href="/f/{forum.id}">
+ <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
+ <span class="navigation-label">{$_(forum.label)}</span>
+ </a>
+ </li>
+ {/each}
</ul>
<style>
import ForumList from './forum_list.svelte';
const internals = {
- results: null
+ results: null
};
describe('Forum List component', () => {
-
- beforeAll(() => {
-
- addMessages('en', {
- 'test_forums.yes': 'Absolutely yes',
- 'test_forums.no': 'No, not at all',
- 'test_forums.maybe': 'OK, maybe...'
- });
- });
-
- beforeEach(() => {
-
- internals.results = render(ForumList, { props: {
- forums: [
- {
- id: 'yes',
- glyph: '☆',
- label: 'test_forums.yes',
- position: 2
- },
- {
- id: 'no',
- glyph: '◯',
- label: 'test_forums.no',
- position: 0
- },
- {
- id: 'maybe',
- glyph: '⏀',
- label: 'test_forums.maybe',
- position: 1
- }
- ]
- } });
- });
-
- test('It should display each forum according to their position', () => {
-
- expect(internals.results.container)
- .toHaveTextContent(/^◯.+⏀.+☆.+$/);
- });
-
- test('It should translate forum labels', () => {
-
- expect(internals.results.getByText('Absolutely yes')).toBeVisible();
- expect(internals.results.getByText('No, not at all')).toBeVisible();
- expect(internals.results.getByText('OK, maybe...')).toBeVisible();
- });
-
- test('It should display forum glyphs', () => {
-
- expect(internals.results.getByText('☆')).toBeVisible();
- expect(internals.results.getByText('◯')).toBeVisible();
- expect(internals.results.getByText('⏀')).toBeVisible();
- });
-
- test('Label should be a permalink to the forum', () => {
-
- expect(internals.results.getByText('Absolutely yes').closest('a'))
- .toHaveAttribute('href', '/f/yes');
- expect(internals.results.getByText('No, not at all').closest('a'))
- .toHaveAttribute('href', '/f/no');
- expect(internals.results.getByText('OK, maybe...').closest('a'))
- .toHaveAttribute('href', '/f/maybe');
- });
-
- test('Glyph should be a permalink to the forum', () => {
-
- expect(internals.results.getByText('☆').closest('a'))
- .toHaveAttribute('href', '/f/yes');
- expect(internals.results.getByText('◯').closest('a'))
- .toHaveAttribute('href', '/f/no');
- expect(internals.results.getByText('⏀').closest('a'))
- .toHaveAttribute('href', '/f/maybe');
- });
+ beforeAll(() => {
+ addMessages('en', {
+ 'test_forums.yes': 'Absolutely yes',
+ 'test_forums.no': 'No, not at all',
+ 'test_forums.maybe': 'OK, maybe...'
+ });
+ });
+
+ beforeEach(() => {
+ internals.results = render(ForumList, {
+ props: {
+ forums: [
+ {
+ id: 'yes',
+ glyph: '☆',
+ label: 'test_forums.yes',
+ position: 2
+ },
+ {
+ id: 'no',
+ glyph: '◯',
+ label: 'test_forums.no',
+ position: 0
+ },
+ {
+ id: 'maybe',
+ glyph: '⏀',
+ label: 'test_forums.maybe',
+ position: 1
+ }
+ ]
+ }
+ });
+ });
+
+ test('It should display each forum according to their position', () => {
+ expect(internals.results.container).toHaveTextContent(/^◯.+⏀.+☆.+$/);
+ });
+
+ test('It should translate forum labels', () => {
+ expect(internals.results.getByText('Absolutely yes')).toBeVisible();
+ expect(internals.results.getByText('No, not at all')).toBeVisible();
+ expect(internals.results.getByText('OK, maybe...')).toBeVisible();
+ });
+
+ test('It should display forum glyphs', () => {
+ expect(internals.results.getByText('☆')).toBeVisible();
+ expect(internals.results.getByText('◯')).toBeVisible();
+ expect(internals.results.getByText('⏀')).toBeVisible();
+ });
+
+ test('Label should be a permalink to the forum', () => {
+ expect(internals.results.getByText('Absolutely yes').closest('a')).toHaveAttribute(
+ 'href',
+ '/f/yes'
+ );
+ expect(internals.results.getByText('No, not at all').closest('a')).toHaveAttribute(
+ 'href',
+ '/f/no'
+ );
+ expect(internals.results.getByText('OK, maybe...').closest('a')).toHaveAttribute(
+ 'href',
+ '/f/maybe'
+ );
+ });
+
+ test('Glyph should be a permalink to the forum', () => {
+ expect(internals.results.getByText('☆').closest('a')).toHaveAttribute('href', '/f/yes');
+ expect(internals.results.getByText('◯').closest('a')).toHaveAttribute('href', '/f/no');
+ expect(internals.results.getByText('⏀').closest('a')).toHaveAttribute('href', '/f/maybe');
+ });
});
import Glyph from './glyph.svelte';
const internals = {
- results: null
+ results: null
};
describe('Glyph component', () => {
-
- beforeEach(() => {
-
- internals.results = render(Glyph, { props: {
- uuid: '9fb14ebc-bc64-400b-915f-d429ec44b8fe'
- } });
- });
-
- test('Should act as an image', () => {
-
- expect(internals.results.getByRole('img'))
- .toBeVisible();
- });
-
- test('Should render 4 glyphs', () => {
-
- expect(internals.results.getByRole('img'))
- .toHaveTextContent(/^. . . .$/);
- });
+ beforeEach(() => {
+ internals.results = render(Glyph, {
+ props: {
+ uuid: '9fb14ebc-bc64-400b-915f-d429ec44b8fe'
+ }
+ });
+ });
+
+ test('Should act as an image', () => {
+ expect(internals.results.getByRole('img')).toBeVisible();
+ });
+
+ test('Should render 4 glyphs', () => {
+ expect(internals.results.getByRole('img')).toHaveTextContent(/^. . . .$/);
+ });
});
>
</li>
{#if $topicActions}
- <TopicActions actions={$topicActions} />
+ <TopicActions actions={$topicActions} />
{/if}
<li>
<a href="/search" title={$_('header.action.search.title')}
import Header from './header.svelte';
describe('Header component', () => {
+ test('Should not display topic if action is not set', () => {
+ const results = render(Header);
- test('Should not display topic if action is not set', () => {
+ expect(results.queryByTitle('Reply')).toBe(null);
+ });
- const results = render(Header);
+ test('Should display topic if action is set', () => {
+ enableTopicActions('d138d6d8-e669-42e7-995d-20a7fcc176f5');
+ const results = render(Header);
- expect(results.queryByTitle('Reply'))
- .toBe(null);
- });
-
- test('Should display topic if action is set', () => {
-
- enableTopicActions('d138d6d8-e669-42e7-995d-20a7fcc176f5');
- const results = render(Header);
-
- expect(results.getByTitle('Reply'))
- .toBeVisible();
- });
+ expect(results.getByTitle('Reply')).toBeVisible();
+ });
});
import { getLangNameFromCode } from 'language-name-map';
$: namedLocales = $locales
- .map((code) => ({
- code,
- ...getLangNameFromCode(code)
- }))
- .sort((a, b) => a.native - b.native);
+ .map((code) => ({
+ code,
+ ...getLangNameFromCode(code)
+ }))
+ .sort((a, b) => a.native - b.native);
- let selected = $locale
+ let selected = $locale;
$: {
- locale.set(selected);
+ locale.set(selected);
}
</script>
import LanguageSelector from './language_selector.svelte';
const internals = {
- results: null
+ results: null
};
describe('Language Selector component', () => {
-
- beforeEach(() => {
-
- internals.results = render(LanguageSelector);
- });
-
- test('Should display languages in their own language', () => {
-
- expect(internals.results.getByText('English'))
- .toBeVisible();
- expect(internals.results.getByText('Español'))
- .toBeVisible();
- });
-
- test('Should change locale when a language is selected', async () => {
-
- locale.subscribe((localeValue) => {
-
- expect(localeValue).toBe('en');
- })();
- const spanish = internals.results.getByText('Español');
- await userEvent.selectOptions(spanish.closest('select'), spanish);
- await act();
- locale.subscribe((localeValue) => {
-
- expect(localeValue).toBe('es');
- })();
- });
+ beforeEach(() => {
+ internals.results = render(LanguageSelector);
+ });
+
+ test('Should display languages in their own language', () => {
+ expect(internals.results.getByText('English')).toBeVisible();
+ expect(internals.results.getByText('Español')).toBeVisible();
+ });
+
+ test('Should change locale when a language is selected', async () => {
+ locale.subscribe((localeValue) => {
+ expect(localeValue).toBe('en');
+ })();
+ const spanish = internals.results.getByText('Español');
+ await userEvent.selectOptions(spanish.closest('select'), spanish);
+ await act();
+ locale.subscribe((localeValue) => {
+ expect(localeValue).toBe('es');
+ })();
+ });
});
-
{timestampToISO(post.created_at)}
</a>
</time>
- {#if post.topic}
- <span>
- ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
- </span>
- {/if}
+ {#if post.topic}
+ <span>
+ ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
+ </span>
+ {/if}
</aside>
<article
class="e-content"
import Post from './post.svelte';
const internals = {
- basicPost: {
- id: 'e5a19d53-4c9a-4be8-afa5-00942ea3afa4',
- text: 'This is an example post qwerty',
- created_at: Date.UTC(2021, 3, 19, 6, 6, 6, 666).valueOf(),
- author: {
- handle: 'very_cool_user',
- id: 'b01bdb48-4b5e-46a4-97f3-6db789bcd33b'
- },
- topic: {
- id: '35d3c3eb-e486-42ef-994c-d8ab1f1e167a',
- title: 'Parent topic, yes'
- }
- },
- postWithoutTopic: {
- id: '9e52e38e-9007-4a20-bbf1-cea4e2f950f3',
- text: 'This is a post without a topic',
- created_at: Date.UTC(2022, 8, 21, 4, 3, 1, 340).valueOf(),
- author: {
- handle: 'my_normal_user',
- id: '121f8f97-de02-4102-b25d-f34fd619009b'
- }
- },
-
- results: null
+ basicPost: {
+ id: 'e5a19d53-4c9a-4be8-afa5-00942ea3afa4',
+ text: 'This is an example post qwerty',
+ created_at: Date.UTC(2021, 3, 19, 6, 6, 6, 666).valueOf(),
+ author: {
+ handle: 'very_cool_user',
+ id: 'b01bdb48-4b5e-46a4-97f3-6db789bcd33b'
+ },
+ topic: {
+ id: '35d3c3eb-e486-42ef-994c-d8ab1f1e167a',
+ title: 'Parent topic, yes'
+ }
+ },
+ postWithoutTopic: {
+ id: '9e52e38e-9007-4a20-bbf1-cea4e2f950f3',
+ text: 'This is a post without a topic',
+ created_at: Date.UTC(2022, 8, 21, 4, 3, 1, 340).valueOf(),
+ author: {
+ handle: 'my_normal_user',
+ id: '121f8f97-de02-4102-b25d-f34fd619009b'
+ }
+ },
+
+ results: null
};
describe('Post component', () => {
-
- beforeEach(() => {
-
- internals.results = render(Post, { props: {
- post: internals.basicPost
- } });
- });
-
- test('Should display the text of the post', () => {
-
- expect(internals.results.getByText('This is an example post qwerty')).toBeVisible();
- });
-
- test('Should display date of the post', () => {
-
- expect(internals.results.getByText('2021-04-19T06:06:06.666Z'))
- .toBeVisible();
- });
-
- test('Date of post should be a permalink to the post', () => {
-
- expect(internals.results.getByText('2021-04-19T06:06:06.666Z').closest('a'))
- .toHaveAttribute('href', '/p/e5a19d53-4c9a-4be8-afa5-00942ea3afa4');
- });
-
- test('Should display the glyph of the post author', () => {
-
- const glyphicon = internals.results.getByRole('img');
-
- expect(glyphicon)
- .toBeVisible();
- expect(glyphicon)
- .toHaveTextContent(/^. . . .$/);
- });
-
- test('Should display author handle', () => {
-
- expect(internals.results.getByText('very_cool_user'))
- .toBeVisible();
- });
-
- test('Author handle should have a permalink to topic', () => {
-
- expect(internals.results.getByText('very_cool_user').closest('a'))
- .toHaveAttribute('href', '/a/very_cool_user');
- });
-
- test('Should display parent topic title', () => {
-
- expect(internals.results.getByText('Parent topic, yes'))
- .toBeVisible();
- });
-
- test('Parent topic title should have a permalink to topic', () => {
-
- expect(internals.results.getByText('Parent topic, yes').closest('a'))
- .toHaveAttribute('href', '/t/35d3c3eb-e486-42ef-994c-d8ab1f1e167a');
- });
-
- test('Parent topic title should have a permalink to topic', () => {
-
- cleanup();
- internals.results = render(Post, { props: {
- post: internals.postWithoutTopic
- } });
-
- expect(internals.results.queryByText('Parent topic, yes'))
- .toBe(null);
- });
-
- test('It should default to 1/1 when no index or count is passed', () => {
-
- expect(internals.results.getByTitle('Post 1 of 1 by very_cool_user'))
- .toBeVisible();
- });
-
- test('Parent topic title should have a permalink to topic', () => {
-
- cleanup();
- internals.results = render(Post, { props: {
- index: 2,
- count: 5,
- post: internals.postWithoutTopic
- } });
-
- expect(internals.results.getByTitle('Post 3 of 5 by my_normal_user'))
- .toBeVisible();
- });
+ beforeEach(() => {
+ internals.results = render(Post, {
+ props: {
+ post: internals.basicPost
+ }
+ });
+ });
+
+ test('Should display the text of the post', () => {
+ expect(internals.results.getByText('This is an example post qwerty')).toBeVisible();
+ });
+
+ test('Should display date of the post', () => {
+ expect(internals.results.getByText('2021-04-19T06:06:06.666Z')).toBeVisible();
+ });
+
+ test('Date of post should be a permalink to the post', () => {
+ expect(internals.results.getByText('2021-04-19T06:06:06.666Z').closest('a')).toHaveAttribute(
+ 'href',
+ '/p/e5a19d53-4c9a-4be8-afa5-00942ea3afa4'
+ );
+ });
+
+ test('Should display the glyph of the post author', () => {
+ const glyphicon = internals.results.getByRole('img');
+
+ expect(glyphicon).toBeVisible();
+ expect(glyphicon).toHaveTextContent(/^. . . .$/);
+ });
+
+ test('Should display author handle', () => {
+ expect(internals.results.getByText('very_cool_user')).toBeVisible();
+ });
+
+ test('Author handle should have a permalink to topic', () => {
+ expect(internals.results.getByText('very_cool_user').closest('a')).toHaveAttribute(
+ 'href',
+ '/a/very_cool_user'
+ );
+ });
+
+ test('Should display parent topic title', () => {
+ expect(internals.results.getByText('Parent topic, yes')).toBeVisible();
+ });
+
+ test('Parent topic title should have a permalink to topic', () => {
+ expect(internals.results.getByText('Parent topic, yes').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/35d3c3eb-e486-42ef-994c-d8ab1f1e167a'
+ );
+ });
+
+ test('Parent topic title should have a permalink to topic', () => {
+ cleanup();
+ internals.results = render(Post, {
+ props: {
+ post: internals.postWithoutTopic
+ }
+ });
+
+ expect(internals.results.queryByText('Parent topic, yes')).toBe(null);
+ });
+
+ test('It should default to 1/1 when no index or count is passed', () => {
+ expect(internals.results.getByTitle('Post 1 of 1 by very_cool_user')).toBeVisible();
+ });
+
+ test('Parent topic title should have a permalink to topic', () => {
+ cleanup();
+ internals.results = render(Post, {
+ props: {
+ index: 2,
+ count: 5,
+ post: internals.postWithoutTopic
+ }
+ });
+
+ expect(internals.results.getByTitle('Post 3 of 5 by my_normal_user')).toBeVisible();
+ });
});
import Tag from './tag.svelte';
const internals = {
- results: null
+ results: null
};
describe('Tag component', () => {
-
- beforeEach(() => {
-
- internals.results = render(Tag, { props: {
- tag: {
- id: 'avocado',
- topics: [
- {
- id: 'eb751e7a-5777-46c3-b81b-cc66546d5157',
- title: 'A single topic',
- ttl: 160 * 1000,
- updated_at: Date.now()
- },
- {
- id: 'b4a5613c-237b-4147-a867-9c105d51e365',
- title: 'And its companion',
- ttl: 160 * 1000,
- updated_at: Date.now()
- }
- ]
- }
- } });
- });
-
- test('It should display the tag title', () => {
-
- expect(internals.results.getByText('Tag: avocado'))
- .toBeVisible();
- });
-
- test('It should display the topics', () => {
-
- expect(internals.results.getByText('A single topic'))
- .toBeVisible();
- expect(internals.results.getByText('And its companion'))
- .toBeVisible();
- });
-
- test('It should link to the topics', () => {
-
- expect(internals.results.getByText('A single topic').closest('a'))
- .toHaveAttribute('href', '/t/eb751e7a-5777-46c3-b81b-cc66546d5157');
- expect(internals.results.getByText('And its companion').closest('a'))
- .toHaveAttribute('href', '/t/b4a5613c-237b-4147-a867-9c105d51e365');
- });
+ beforeEach(() => {
+ internals.results = render(Tag, {
+ props: {
+ tag: {
+ id: 'avocado',
+ topics: [
+ {
+ id: 'eb751e7a-5777-46c3-b81b-cc66546d5157',
+ title: 'A single topic',
+ ttl: 160 * 1000,
+ updated_at: Date.now()
+ },
+ {
+ id: 'b4a5613c-237b-4147-a867-9c105d51e365',
+ title: 'And its companion',
+ ttl: 160 * 1000,
+ updated_at: Date.now()
+ }
+ ]
+ }
+ }
+ });
+ });
+
+ test('It should display the tag title', () => {
+ expect(internals.results.getByText('Tag: avocado')).toBeVisible();
+ });
+
+ test('It should display the topics', () => {
+ expect(internals.results.getByText('A single topic')).toBeVisible();
+ expect(internals.results.getByText('And its companion')).toBeVisible();
+ });
+
+ test('It should link to the topics', () => {
+ expect(internals.results.getByText('A single topic').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/eb751e7a-5777-46c3-b81b-cc66546d5157'
+ );
+ expect(internals.results.getByText('And its companion').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/b4a5613c-237b-4147-a867-9c105d51e365'
+ );
+ });
});
<aside class="topic-meta" title={$_('topic.metadata_title')}>
{#if topic.forum}
<span class="topic-location">
- {$_('topic.category_location')}
+ {$_('topic.category_location')}
<a href="/f/{topic.forum.id}" class="p-category">
- {topic.forum.glyph} {$_(topic.forum.label)}
- </a>.
- </span>
+ {topic.forum.glyph}
+ {$_(topic.forum.label)}
+ </a>.
+ </span>
{/if}
<span class="topic-ttl">
- <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}">
- ({$_('topic.remaining_time', {
+ <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}">
+ ({$_('topic.remaining_time', {
values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) }
- })})
- </a>.
- </span>
+ })})
+ </a>.
+ </span>
</aside>
{#if topic.tags.length > 0}
<aside class="topic-tags" title={$_('topic.tags_title')}>
{$_('topic.tags_location')}
{#each topic.tags as tag}
<a href="/g/{tag.id}" class="p-category">
- {tag.id}<span class="tag-weight">({tag.weight})</span>
- </a>{' '}
+ {tag.id}<span class="tag-weight">({tag.weight})</span>
+ </a>{' '}
{/each}
</aside>
{/if}
import Topic from './topic.svelte';
const internals = {
- results: null,
- basicTopic: {
- id: 'b1a4f8d1-4d16-4872-b391-fda6a0e9012d',
- title: 'I sure am a test topic',
- ttl: 160 * 1000,
- updated_at: Date.now(),
- forum: {
- id: 'diversion',
- glyph: '⏃',
- label: 'test_forums.diversion'
- },
- tags: [
- {
- id: 'fish',
- weight: 40
- },
- {
- id: 'statue',
- weight: 5
- }
- ],
- posts: [
- {
- id: '413a74db-9473-4bac-8698-da9452c05854',
- text: 'This is the first post',
- created_at: Date.UTC(1999, 7, 1, 8, 8, 2, 111).valueOf(),
- author: {
- handle: 'past_user',
- id: 'c76d3e51-76ac-4e84-a1b2-2eee9abd68b3'
- }
- },
- {
- id: '821ff177-5250-406f-9431-1a8097b35430',
- text: 'This response came later',
- created_at: Date.UTC(2038, 1, 2, 3, 4, 6, 789).valueOf(),
- author: {
- handle: 'future_user',
- id: 'cb9307cb-77e9-4c55-bbe7-dbbf88737358'
- }
- }
- ]
- },
- topicWithoutForum: {
- id: '9715e9ee-0d63-4b50-b613-826ef2791728',
- title: 'This topic, no forums',
- ttl: 160 * 1000,
- updated_at: Date.now(),
- tags: [
- {
- id: 'cauliflower',
- weight: 33
- }
- ],
- posts: []
- }
+ results: null,
+ basicTopic: {
+ id: 'b1a4f8d1-4d16-4872-b391-fda6a0e9012d',
+ title: 'I sure am a test topic',
+ ttl: 160 * 1000,
+ updated_at: Date.now(),
+ forum: {
+ id: 'diversion',
+ glyph: '⏃',
+ label: 'test_forums.diversion'
+ },
+ tags: [
+ {
+ id: 'fish',
+ weight: 40
+ },
+ {
+ id: 'statue',
+ weight: 5
+ }
+ ],
+ posts: [
+ {
+ id: '413a74db-9473-4bac-8698-da9452c05854',
+ text: 'This is the first post',
+ created_at: Date.UTC(1999, 7, 1, 8, 8, 2, 111).valueOf(),
+ author: {
+ handle: 'past_user',
+ id: 'c76d3e51-76ac-4e84-a1b2-2eee9abd68b3'
+ }
+ },
+ {
+ id: '821ff177-5250-406f-9431-1a8097b35430',
+ text: 'This response came later',
+ created_at: Date.UTC(2038, 1, 2, 3, 4, 6, 789).valueOf(),
+ author: {
+ handle: 'future_user',
+ id: 'cb9307cb-77e9-4c55-bbe7-dbbf88737358'
+ }
+ }
+ ]
+ },
+ topicWithoutForum: {
+ id: '9715e9ee-0d63-4b50-b613-826ef2791728',
+ title: 'This topic, no forums',
+ ttl: 160 * 1000,
+ updated_at: Date.now(),
+ tags: [
+ {
+ id: 'cauliflower',
+ weight: 33
+ }
+ ],
+ posts: []
+ }
};
describe('Topic component', () => {
-
- beforeAll(() => {
-
- addMessages('en', {
- 'test_forums.diversion': 'Diversion'
- });
- });
-
- beforeEach(() => {
-
- internals.results = render(Topic, { props: {
- topic: internals.basicTopic
- } });
- });
-
- test('Should show the topic title', () => {
-
- expect(internals.results.getByText('I sure am a test topic'))
- .toBeVisible();
- });
- test('Should display remaining time in readable format', () => {
-
- expect(internals.results.getByText(/2 minutes remaining/))
- .toBeVisible();
- });
- test('Remaining time should be a permalink to the topic', () => {
-
- expect(internals.results.getByText(/2 minutes remaining/).closest('a'))
- .toHaveAttribute('href', '/t/b1a4f8d1-4d16-4872-b391-fda6a0e9012d');
- });
-
- test('Should show text for all posts', () => {
-
- expect(internals.results.getByText('This is the first post'))
- .toBeVisible();
- expect(internals.results.getByText('This response came later'))
- .toBeVisible();
- });
-
- test('Should send index and count to posts', () => {
-
- expect(internals.results.getByTitle('Post 1 of 2 by past_user'))
- .toBeVisible();
- expect(internals.results.getByTitle('Post 2 of 2 by future_user'))
- .toBeVisible();
- });
-
- describe('Forum link', () => {
-
- test('Should show forum if the post has one', () => {
-
- expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/))
- .toBeVisible();
- });
-
- test('Forum text should be a permalink to the forum', () => {
-
- expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/).closest('a'))
- .toHaveAttribute('href', '/f/diversion');
- });
-
- test('Should not show forum if the post doesn\'t have one', () => {
-
- cleanup();
- internals.results = render(Topic, { props: {
- topic: internals.topicWithoutForum
- } });
-
- expect(internals.results.queryByText(/^\s*⏃\s*Diversion\s*$/))
- .toBe(null);
- });
- });
-
- describe('Tag listing', () => {
-
- test('Should show topic tags', () => {
-
- expect(internals.results.getByText('fish'))
- .toBeVisible();
- expect(internals.results.getByText('fish'))
- .toHaveTextContent('fish(40)');
- expect(internals.results.getByText('statue'))
- .toBeVisible();
- expect(internals.results.getByText('statue'))
- .toHaveTextContent('statue(5)');
- });
-
- test('Tag text should be a permalink to the tag', () => {
-
- expect(internals.results.getByText('fish').closest('a'))
- .toHaveAttribute('href', '/g/fish');
- expect(internals.results.getByText('statue').closest('a'))
- .toHaveAttribute('href', '/g/statue');
- });
- });
+ beforeAll(() => {
+ addMessages('en', {
+ 'test_forums.diversion': 'Diversion'
+ });
+ });
+
+ beforeEach(() => {
+ internals.results = render(Topic, {
+ props: {
+ topic: internals.basicTopic
+ }
+ });
+ });
+
+ test('Should show the topic title', () => {
+ expect(internals.results.getByText('I sure am a test topic')).toBeVisible();
+ });
+ test('Should display remaining time in readable format', () => {
+ expect(internals.results.getByText(/2 minutes remaining/)).toBeVisible();
+ });
+ test('Remaining time should be a permalink to the topic', () => {
+ expect(internals.results.getByText(/2 minutes remaining/).closest('a')).toHaveAttribute(
+ 'href',
+ '/t/b1a4f8d1-4d16-4872-b391-fda6a0e9012d'
+ );
+ });
+
+ test('Should show text for all posts', () => {
+ expect(internals.results.getByText('This is the first post')).toBeVisible();
+ expect(internals.results.getByText('This response came later')).toBeVisible();
+ });
+
+ test('Should send index and count to posts', () => {
+ expect(internals.results.getByTitle('Post 1 of 2 by past_user')).toBeVisible();
+ expect(internals.results.getByTitle('Post 2 of 2 by future_user')).toBeVisible();
+ });
+
+ describe('Forum link', () => {
+ test('Should show forum if the post has one', () => {
+ expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/)).toBeVisible();
+ });
+
+ test('Forum text should be a permalink to the forum', () => {
+ expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/).closest('a')).toHaveAttribute(
+ 'href',
+ '/f/diversion'
+ );
+ });
+
+ test("Should not show forum if the post doesn't have one", () => {
+ cleanup();
+ internals.results = render(Topic, {
+ props: {
+ topic: internals.topicWithoutForum
+ }
+ });
+
+ expect(internals.results.queryByText(/^\s*⏃\s*Diversion\s*$/)).toBe(null);
+ });
+ });
+
+ describe('Tag listing', () => {
+ test('Should show topic tags', () => {
+ expect(internals.results.getByText('fish')).toBeVisible();
+ expect(internals.results.getByText('fish')).toHaveTextContent('fish(40)');
+ expect(internals.results.getByText('statue')).toBeVisible();
+ expect(internals.results.getByText('statue')).toHaveTextContent('statue(5)');
+ });
+
+ test('Tag text should be a permalink to the tag', () => {
+ expect(internals.results.getByText('fish').closest('a')).toHaveAttribute('href', '/g/fish');
+ expect(internals.results.getByText('statue').closest('a')).toHaveAttribute(
+ 'href',
+ '/g/statue'
+ );
+ });
+ });
});
</script>
<li class="h-entry" title={$_('topic.title')}>
- <span class="p-name">
- <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}">
- {topic.title}
- </a></span>
- <span class="topic-ttl">({$_('topic.remaining_time', {
+ <span class="p-name">
+ <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}">
+ {topic.title}
+ </a></span
+ >
+ <span class="topic-ttl"
+ >({$_('topic.remaining_time', {
values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) }
- })})
- </span>
+ })})
+ </span>
</li>
<style>
import TopicSummary from './topic_summary.svelte';
const internals = {
- results: null
+ results: null
};
describe('Topic Summary component', () => {
-
- beforeEach(() => {
-
- internals.results = render(TopicSummary, { props: {
- topic: {
- id: 'ea2431c8-5c1c-4ed0-907a-45e012696ab8',
- title: 'I sure am a test topic',
- ttl: 160 * 1000,
- updated_at: Date.now()
- }
- } });
- });
-
- test('It should display the title', () => {
-
- expect(internals.results.getByText('I sure am a test topic'))
- .toBeVisible();
- });
-
- test('Topic title should be a permalink', () => {
-
- expect(internals.results.getByText('I sure am a test topic').closest('a'))
- .toHaveAttribute('href', '/t/ea2431c8-5c1c-4ed0-907a-45e012696ab8');
- });
-
- test('It should display remaining time in readable format', () => {
-
- expect(internals.results.getByText(/2 minutes remaining/))
- .toBeVisible();
- });
+ beforeEach(() => {
+ internals.results = render(TopicSummary, {
+ props: {
+ topic: {
+ id: 'ea2431c8-5c1c-4ed0-907a-45e012696ab8',
+ title: 'I sure am a test topic',
+ ttl: 160 * 1000,
+ updated_at: Date.now()
+ }
+ }
+ });
+ });
+
+ test('It should display the title', () => {
+ expect(internals.results.getByText('I sure am a test topic')).toBeVisible();
+ });
+
+ test('Topic title should be a permalink', () => {
+ expect(internals.results.getByText('I sure am a test topic').closest('a')).toHaveAttribute(
+ 'href',
+ '/t/ea2431c8-5c1c-4ed0-907a-45e012696ab8'
+ );
+ });
+
+ test('It should display remaining time in readable format', () => {
+ expect(internals.results.getByText(/2 minutes remaining/)).toBeVisible();
+ });
});
export const apollo = {
- uri: 'http://127.0.0.1:1234/graphql',
- name: 'COOL_APP',
- version: '9.9.9',
- defaultOptions: {
- watchQuery: {
- fetchPolicy: 'no-cache'
- },
- query: {
- fetchPolicy: 'no-cache'
- }
- }
+ uri: 'http://127.0.0.1:1234/graphql',
+ name: 'COOL_APP',
+ version: '9.9.9',
+ defaultOptions: {
+ watchQuery: {
+ fetchPolicy: 'no-cache'
+ },
+ query: {
+ fetchPolicy: 'no-cache'
+ }
+ }
};
export const version = '9.9.9';
const cache = new InMemoryCache();
export const client = new ApolloClient({
- cache,
- link: new HttpLink({
- uri: apolloConfig.uri,
- fetch
- }),
- ...apolloConfig
+ cache,
+ link: new HttpLink({
+ uri: apolloConfig.uri,
+ fetch
+ }),
+ ...apolloConfig
});
const internals = {
- version: '1.0.0'
+ version: '1.0.0'
};
/*
*/
export const apollo = {
- uri: import.meta.env.VITE_APOLLO_SERVER,
- name: 'forum',
- version: internals.version
+ uri: import.meta.env.VITE_APOLLO_SERVER,
+ name: 'forum',
+ version: internals.version
};
export const version = internals.version;
addMessages('es', es);
init({
- fallbackLocale: 'en',
- initialLocale: getLocaleFromNavigator().replace(/-[A-Z]{2}$/, '')
+ fallbackLocale: 'en',
+ initialLocale: getLocaleFromNavigator().replace(/-[A-Z]{2}$/, '')
});
import { enableTopicActions, disableTopicActions, topicActions } from './actions';
describe('Topic actions and state', () => {
-
- test('There should be no topic actions by default', () => {
-
- topicActions.subscribe((actions) => {
-
- expect(actions).toBe(undefined);
- })();
- });
-
- test('enableTopicActions should set the topic id', () => {
-
- enableTopicActions('free_hat');
- topicActions.subscribe((actions) => {
-
- expect(actions).toEqual({
- id: 'free_hat'
- });
- })();
- });
-
- test('disableTopicActions should unset the topic id', () => {
-
- disableTopicActions();
- topicActions.subscribe((actions) => {
-
- expect(actions).toEqual(undefined);
- })();
- });
+ test('There should be no topic actions by default', () => {
+ topicActions.subscribe((actions) => {
+ expect(actions).toBe(undefined);
+ })();
+ });
+
+ test('enableTopicActions should set the topic id', () => {
+ enableTopicActions('free_hat');
+ topicActions.subscribe((actions) => {
+ expect(actions).toEqual({
+ id: 'free_hat'
+ });
+ })();
+ });
+
+ test('disableTopicActions should unset the topic id', () => {
+ disableTopicActions();
+ topicActions.subscribe((actions) => {
+ expect(actions).toEqual(undefined);
+ })();
+ });
});
import type { Readable, Writable } from 'svelte/store';
export type Actions = {
- topic?: TopicAction
+ topic?: TopicAction;
};
export type TopicAction = {
- id: string
+ id: string;
};
/*
const actions: Writable<Actions> = writable({});
export const enableTopicActions = (id: string) => {
-
- actions.update((actionsValue: Actions): Actions => {
-
- actionsValue.topic = {
- id
- };
- return actionsValue;
- });
+ actions.update((actionsValue: Actions): Actions => {
+ actionsValue.topic = {
+ id
+ };
+ return actionsValue;
+ });
};
export const disableTopicActions = () => {
-
- actions.update((actionsValue): Actions => {
-
- delete actionsValue.topic;
- return actionsValue;
- });
+ actions.update((actionsValue): Actions => {
+ delete actionsValue.topic;
+ return actionsValue;
+ });
};
-export const topicActions: Readable<TopicAction> = derived(
- actions,
- ($actions) => $actions.topic
-);
+export const topicActions: Readable<TopicAction> = derived(actions, ($actions) => $actions.topic);
*/
type ApolloStoreConfiguration<Type> = {
- key: string,
- query: DocumentNode,
- initialValue?: Type | void
- variables?: object
+ key: string;
+ query: DocumentNode;
+ initialValue?: Type | void;
+ variables?: object;
};
type ApolloStoreState<Type> = {
- loading: boolean,
- data: Type | void,
- error: Error | void
+ loading: boolean;
+ data: Type | void;
+ error: Error | void;
};
-export const store = function store<Type>({ key, query, initialValue = null, variables = {} }: ApolloStoreConfiguration<Type>): Readable<ApolloStoreState<Type>> {
-
- const initialState: ApolloStoreState<Type> = {
- loading: true,
- data: initialValue,
- error: undefined
- };
-
- return readable(
- initialState,
- (set) => {
-
- const handleError = function (error: Error) {
-
- return set({
- loading: false,
- data: initialValue,
- error
- });
- };
-
- client.watchQuery({ query, variables }).subscribe(
- (result: ApolloQueryResult<Type>) => {
-
- if (result.errors) {
- const error = new ApolloError({ graphQLErrors: result.errors });
- return handleError(error);
- }
-
- set({
- loading: false,
- data: result.data[key],
- error: undefined
- });
- },
- (error: Error) => handleError(error)
- );
- }
- );
+export const store = function store<Type>({
+ key,
+ query,
+ initialValue = null,
+ variables = {}
+}: ApolloStoreConfiguration<Type>): Readable<ApolloStoreState<Type>> {
+ const initialState: ApolloStoreState<Type> = {
+ loading: true,
+ data: initialValue,
+ error: undefined
+ };
+
+ return readable(initialState, (set) => {
+ const handleError = function (error: Error) {
+ return set({
+ loading: false,
+ data: initialValue,
+ error
+ });
+ };
+
+ client.watchQuery({ query, variables }).subscribe(
+ (result: ApolloQueryResult<Type>) => {
+ if (result.errors) {
+ const error = new ApolloError({ graphQLErrors: result.errors });
+ return handleError(error);
+ }
+
+ set({
+ loading: false,
+ data: result.data[key],
+ error: undefined
+ });
+ },
+ (error: Error) => handleError(error)
+ );
+ });
};
import { getForum, getForums } from './forums';
const internals = {
- provider: null
+ provider: null
};
describe('Forums store pact', () => {
-
- beforeAll(async () => {
-
- internals.provider = new Pact({
- port: 1234,
- dir: resolve(process.cwd(), 'pacts'),
- consumer: 'ForumClient',
- provider: 'ForumServer',
- pactfileWriteMode: 'update'
- });
-
- await internals.provider.setup();
- });
-
- afterEach(() => internals.provider.verify());
- afterAll(() => internals.provider.finalize());
-
- describe('When there\'s data', () => {
-
- describe('GetForums', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s data')
- .uponReceiving('a request to list the forums')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForums')
- .withQuery(
- `query GetForums {
+ beforeAll(async () => {
+ internals.provider = new Pact({
+ port: 1234,
+ dir: resolve(process.cwd(), 'pacts'),
+ consumer: 'ForumClient',
+ provider: 'ForumServer',
+ pactfileWriteMode: 'update'
+ });
+
+ await internals.provider.setup();
+ });
+
+ afterEach(() => internals.provider.verify());
+ afterAll(() => internals.provider.finalize());
+
+ describe("When there's data", () => {
+ describe('GetForums', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's data")
+ .uponReceiving('a request to list the forums')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForums')
+ .withQuery(
+ `query GetForums {
forums {
id
glyph
__typename
}
}`
- )
- .withVariables({})
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- forums: eachLike({
- id: like('butter'),
- glyph: like('⌘'),
- label: like('test_forums.butter'),
- position: like(1)
- })
- }
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the forums', async () => {
-
- const forums = getForums();
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forums.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toEqual(expect.arrayContaining([{
- id: 'butter',
- glyph: '⌘',
- label: 'test_forums.butter',
- position: 1
- }]));
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
-
- describe('GetForum', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s data')
- .uponReceiving('a request to get a single forum')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForum')
- .withQuery(
- `query GetForum($id: ID!) {
+ )
+ .withVariables({})
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ forums: eachLike({
+ id: like('butter'),
+ glyph: like('⌘'),
+ label: like('test_forums.butter'),
+ position: like(1)
+ })
+ }
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the forums', async () => {
+ const forums = getForums();
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forums.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toEqual(
+ expect.arrayContaining([
+ {
+ id: 'butter',
+ glyph: '⌘',
+ label: 'test_forums.butter',
+ position: 1
+ }
+ ])
+ );
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+
+ describe('GetForum', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's data")
+ .uponReceiving('a request to get a single forum')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForum')
+ .withQuery(
+ `query GetForum($id: ID!) {
forum(id: $id) {
id
glyph
__typename
}
}`
- )
- .withVariables({
- id: 'freezer'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- forum: like({
- id: 'freezer',
- glyph: like('✭'),
- label: like('test_forums.freezer'),
- position: like(3),
- topics: eachLike({
- id: like('629de02c-151a-4db7-bb86-43b2add8a15a'),
- title: like('Very pacty topic'),
- updated_at: like(1619954611616),
- ttl: like(3601)
- })
- })
- }
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the forum', async () => {
-
- const forum = getForum('freezer');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forum.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data.id).toBe('freezer');
- expect(response.data.glyph).toBe('✭');
- expect(response.data.label).toBe('test_forums.freezer');
- expect(response.data.position).toBe(3);
- expect(response.data.topics).toEqual(expect.arrayContaining([{
- id: '629de02c-151a-4db7-bb86-43b2add8a15a',
- title: 'Very pacty topic',
- updated_at: 1619954611616,
- ttl: 3601
- }]));
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s no data', () => {
-
- describe('GetForums', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s no data')
- .uponReceiving('a request to list the forums')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForums')
- .withQuery(
- `query GetForums {
+ )
+ .withVariables({
+ id: 'freezer'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ forum: like({
+ id: 'freezer',
+ glyph: like('✭'),
+ label: like('test_forums.freezer'),
+ position: like(3),
+ topics: eachLike({
+ id: like('629de02c-151a-4db7-bb86-43b2add8a15a'),
+ title: like('Very pacty topic'),
+ updated_at: like(1619954611616),
+ ttl: like(3601)
+ })
+ })
+ }
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the forum', async () => {
+ const forum = getForum('freezer');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forum.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data.id).toBe('freezer');
+ expect(response.data.glyph).toBe('✭');
+ expect(response.data.label).toBe('test_forums.freezer');
+ expect(response.data.position).toBe(3);
+ expect(response.data.topics).toEqual(
+ expect.arrayContaining([
+ {
+ id: '629de02c-151a-4db7-bb86-43b2add8a15a',
+ title: 'Very pacty topic',
+ updated_at: 1619954611616,
+ ttl: 3601
+ }
+ ])
+ );
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's no data", () => {
+ describe('GetForums', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's no data")
+ .uponReceiving('a request to list the forums')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForums')
+ .withQuery(
+ `query GetForums {
forums {
id
glyph
__typename
}
}`
- )
- .withVariables({})
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- forums: []
- }
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the forums', async () => {
-
- const forums = getForums();
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forums.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
-
- describe('GetForum', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s no data')
- .uponReceiving('a request to get a single forum')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForum')
- .withQuery(
- `query GetForum($id: ID!) {
+ )
+ .withVariables({})
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ forums: []
+ }
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the forums', async () => {
+ const forums = getForums();
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forums.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+
+ describe('GetForum', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's no data")
+ .uponReceiving('a request to get a single forum')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForum')
+ .withQuery(
+ `query GetForum($id: ID!) {
forum(id: $id) {
id
glyph
__typename
}
}`
- )
- .withVariables({
- id: 'freezer'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- forum: null
- }
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the forum', async () => {
-
- const forum = getForum('freezer');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forum.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s a server error', () => {
-
- describe('GetForums', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s a server error')
- .uponReceiving('a request to list the forums')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForums')
- .withQuery(
- `query GetForums {
+ )
+ .withVariables({
+ id: 'freezer'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ forum: null
+ }
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the forum', async () => {
+ const forum = getForum('freezer');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forum.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's a server error", () => {
+ describe('GetForums', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's a server error")
+ .uponReceiving('a request to list the forums')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForums')
+ .withQuery(
+ `query GetForums {
forums {
id
glyph
__typename
}
}`
- )
- .withVariables({})
- .willRespondWith({
- status: 500
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the error', async () => {
-
- const forums = getForums();
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forums.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(false);
- expect(response.error).toBeInstanceOf(Error);
- });
- });
-
- describe('GetForum', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s a server error')
- .uponReceiving('a request to get a single forum')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForum')
- .withQuery(
- `query GetForum($id: ID!) {
+ )
+ .withVariables({})
+ .willRespondWith({
+ status: 500
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the error', async () => {
+ const forums = getForums();
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forums.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBeInstanceOf(Error);
+ });
+ });
+
+ describe('GetForum', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's a server error")
+ .uponReceiving('a request to get a single forum')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForum')
+ .withQuery(
+ `query GetForum($id: ID!) {
forum(id: $id) {
id
glyph
__typename
}
}`
- )
- .withVariables({
- id: 'freezer'
- })
- .willRespondWith({
- status: 500
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the error', async () => {
-
- const forum = getForum('freezer');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forum.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBeInstanceOf(Error);
- });
- });
- });
-
- describe('When there\'s an error in the response', () => {
-
- describe('GetForums', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s an error in the response')
- .uponReceiving('a request to list the forums')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForums')
- .withQuery(
- `query GetForums {
+ )
+ .withVariables({
+ id: 'freezer'
+ })
+ .willRespondWith({
+ status: 500
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the error', async () => {
+ const forum = getForum('freezer');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forum.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBeInstanceOf(Error);
+ });
+ });
+ });
+
+ describe("When there's an error in the response", () => {
+ describe('GetForums', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's an error in the response")
+ .uponReceiving('a request to list the forums')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForums')
+ .withQuery(
+ `query GetForums {
forums {
id
glyph
__typename
}
}`
- )
- .withVariables({})
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- errors: eachLike({
- message: like('An error occurred when fetching forums')
- })
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the error', async () => {
-
- const forums = getForums();
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forums.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBeInstanceOf(Array);
- expect(response.data.length).toBe(0);
- expect(response.loading).toBe(false);
- expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{
- message: 'An error occurred when fetching forums'
- }]));
- });
- });
-
- describe('GetForum', () => {
-
- beforeAll(async () => {
-
- const forumQuery = new GraphQLInteraction()
- .given('there\'s an error in the response')
- .uponReceiving('a request to get a single forum')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetForum')
- .withQuery(
- `query GetForum($id: ID!) {
+ )
+ .withVariables({})
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ errors: eachLike({
+ message: like('An error occurred when fetching forums')
+ })
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the error', async () => {
+ const forums = getForums();
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forums.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBeInstanceOf(Array);
+ expect(response.data.length).toBe(0);
+ expect(response.loading).toBe(false);
+ expect(response.error.graphQLErrors).toEqual(
+ expect.arrayContaining([
+ {
+ message: 'An error occurred when fetching forums'
+ }
+ ])
+ );
+ });
+ });
+
+ describe('GetForum', () => {
+ beforeAll(async () => {
+ const forumQuery = new GraphQLInteraction()
+ .given("there's an error in the response")
+ .uponReceiving('a request to get a single forum')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetForum')
+ .withQuery(
+ `query GetForum($id: ID!) {
forum(id: $id) {
id
glyph
__typename
}
}`
- )
- .withVariables({
- id: 'freezer'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- errors: eachLike({
- message: like('An error occurred when fetching the forum')
- })
- }
- });
- return await internals.provider.addInteraction(forumQuery);
- });
-
- test('it returns the error', async () => {
-
- const forum = getForum('freezer');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- forum.subscribe((forumsValue) => {
-
- response = forumsValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{
- message: 'An error occurred when fetching the forum'
- }]));
- });
- });
- });
+ )
+ .withVariables({
+ id: 'freezer'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ errors: eachLike({
+ message: like('An error occurred when fetching the forum')
+ })
+ }
+ });
+ return await internals.provider.addInteraction(forumQuery);
+ });
+
+ test('it returns the error', async () => {
+ const forum = getForum('freezer');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ forum.subscribe((forumsValue) => {
+ response = forumsValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error.graphQLErrors).toEqual(
+ expect.arrayContaining([
+ {
+ message: 'An error occurred when fetching the forum'
+ }
+ ])
+ );
+ });
+ });
+ });
});
import { store } from './apollo';
import { GET_FORUM, GET_FORUMS } from '$lib/data/queries';
-export const getForum = (id: string) => store({ key: 'forum', query: GET_FORUM, variables: { id } });
+export const getForum = (id: string) =>
+ store({ key: 'forum', query: GET_FORUM, variables: { id } });
export const getForums = () => store({ key: 'forums', query: GET_FORUMS, initialValue: [] });
import { getPost } from './posts';
const internals = {
- provider: null
+ provider: null
};
describe('Posts store pact', () => {
-
- beforeAll(async () => {
-
- internals.provider = new Pact({
- port: 1234,
- dir: resolve(process.cwd(), 'pacts'),
- consumer: 'ForumClient',
- provider: 'ForumServer',
- pactfileWriteMode: 'update'
- });
-
- await internals.provider.setup();
- });
-
- afterEach(() => internals.provider.verify());
- afterAll(() => internals.provider.finalize());
-
- describe('When there\'s data', () => {
-
- describe('GetPost', () => {
-
- beforeAll(async () => {
-
- const postQuery = new GraphQLInteraction()
- .given('there\'s data')
- .uponReceiving('a request to get a single post')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetPost')
- .withQuery(
- `query GetPost($id: ID!) {
+ beforeAll(async () => {
+ internals.provider = new Pact({
+ port: 1234,
+ dir: resolve(process.cwd(), 'pacts'),
+ consumer: 'ForumClient',
+ provider: 'ForumServer',
+ pactfileWriteMode: 'update'
+ });
+
+ await internals.provider.setup();
+ });
+
+ afterEach(() => internals.provider.verify());
+ afterAll(() => internals.provider.finalize());
+
+ describe("When there's data", () => {
+ describe('GetPost', () => {
+ beforeAll(async () => {
+ const postQuery = new GraphQLInteraction()
+ .given("there's data")
+ .uponReceiving('a request to get a single post')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetPost')
+ .withQuery(
+ `query GetPost($id: ID!) {
post(id: $id) {
id
text
__typename
}
}`
- )
- .withVariables({
- id: '8f75eba5-6989-4dd3-b466-e464546ce374'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- post: like({
- id: like('8f75eba5-6989-4dd3-b466-e464546ce374'),
- text: like('This is a very pacty post'),
- created_at: like(1619976194937),
- author: like({
- id: like('a805b3de-cac4-451c-a1e6-f078869c9db9'),
- handle: like('pacts_person')
- }),
- topic: like({
- id: like('5c283ce1-0470-4b98-86f5-1fec9a22c9ac'),
- title: like('The parent pacts topic')
- })
- })
- }
- }
- });
- return await internals.provider.addInteraction(postQuery);
- });
-
- test('it returns the post', async () => {
-
- const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- post.subscribe((postValue) => {
-
- response = postValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toEqual({
- id: '8f75eba5-6989-4dd3-b466-e464546ce374',
- text: 'This is a very pacty post',
- created_at: 1619976194937,
- author: {
- id: 'a805b3de-cac4-451c-a1e6-f078869c9db9',
- handle: 'pacts_person'
- },
- topic: {
- id: '5c283ce1-0470-4b98-86f5-1fec9a22c9ac',
- title: 'The parent pacts topic'
- }
- });
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s no data', () => {
-
- describe('GetPost', () => {
-
- beforeAll(async () => {
-
- const postQuery = new GraphQLInteraction()
- .given('there\'s no data')
- .uponReceiving('a request to get a single post')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetPost')
- .withQuery(
- `query GetPost($id: ID!) {
+ )
+ .withVariables({
+ id: '8f75eba5-6989-4dd3-b466-e464546ce374'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ post: like({
+ id: like('8f75eba5-6989-4dd3-b466-e464546ce374'),
+ text: like('This is a very pacty post'),
+ created_at: like(1619976194937),
+ author: like({
+ id: like('a805b3de-cac4-451c-a1e6-f078869c9db9'),
+ handle: like('pacts_person')
+ }),
+ topic: like({
+ id: like('5c283ce1-0470-4b98-86f5-1fec9a22c9ac'),
+ title: like('The parent pacts topic')
+ })
+ })
+ }
+ }
+ });
+ return await internals.provider.addInteraction(postQuery);
+ });
+
+ test('it returns the post', async () => {
+ const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ post.subscribe((postValue) => {
+ response = postValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toEqual({
+ id: '8f75eba5-6989-4dd3-b466-e464546ce374',
+ text: 'This is a very pacty post',
+ created_at: 1619976194937,
+ author: {
+ id: 'a805b3de-cac4-451c-a1e6-f078869c9db9',
+ handle: 'pacts_person'
+ },
+ topic: {
+ id: '5c283ce1-0470-4b98-86f5-1fec9a22c9ac',
+ title: 'The parent pacts topic'
+ }
+ });
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's no data", () => {
+ describe('GetPost', () => {
+ beforeAll(async () => {
+ const postQuery = new GraphQLInteraction()
+ .given("there's no data")
+ .uponReceiving('a request to get a single post')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetPost')
+ .withQuery(
+ `query GetPost($id: ID!) {
post(id: $id) {
id
text
__typename
}
}`
- )
- .withVariables({
- id: '8f75eba5-6989-4dd3-b466-e464546ce374'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- post: null
- }
- }
- });
- return await internals.provider.addInteraction(postQuery);
- });
-
- test('it returns the post', async () => {
-
- const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- post.subscribe((postValue) => {
-
- response = postValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s a server error', () => {
-
- describe('GetPost', () => {
-
- beforeAll(async () => {
-
- const postQuery = new GraphQLInteraction()
- .given('there\'s a server error')
- .uponReceiving('a request to get a single post')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetPost')
- .withQuery(
- `query GetPost($id: ID!) {
+ )
+ .withVariables({
+ id: '8f75eba5-6989-4dd3-b466-e464546ce374'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ post: null
+ }
+ }
+ });
+ return await internals.provider.addInteraction(postQuery);
+ });
+
+ test('it returns the post', async () => {
+ const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ post.subscribe((postValue) => {
+ response = postValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's a server error", () => {
+ describe('GetPost', () => {
+ beforeAll(async () => {
+ const postQuery = new GraphQLInteraction()
+ .given("there's a server error")
+ .uponReceiving('a request to get a single post')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetPost')
+ .withQuery(
+ `query GetPost($id: ID!) {
post(id: $id) {
id
text
__typename
}
}`
- )
- .withVariables({
- id: '8f75eba5-6989-4dd3-b466-e464546ce374'
- })
- .willRespondWith({
- status: 500
- });
- return await internals.provider.addInteraction(postQuery);
- });
-
- test('it returns the error', async () => {
-
- const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- post.subscribe((postValue) => {
-
- response = postValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBeInstanceOf(Error);
- });
- });
- });
-
- describe('When there\'s an error in the response', () => {
-
- describe('GetPost', () => {
-
- beforeAll(async () => {
-
- const postQuery = new GraphQLInteraction()
- .given('there\'s an error in the response')
- .uponReceiving('a request to get a single post')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetPost')
- .withQuery(
- `query GetPost($id: ID!) {
+ )
+ .withVariables({
+ id: '8f75eba5-6989-4dd3-b466-e464546ce374'
+ })
+ .willRespondWith({
+ status: 500
+ });
+ return await internals.provider.addInteraction(postQuery);
+ });
+
+ test('it returns the error', async () => {
+ const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ post.subscribe((postValue) => {
+ response = postValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBeInstanceOf(Error);
+ });
+ });
+ });
+
+ describe("When there's an error in the response", () => {
+ describe('GetPost', () => {
+ beforeAll(async () => {
+ const postQuery = new GraphQLInteraction()
+ .given("there's an error in the response")
+ .uponReceiving('a request to get a single post')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetPost')
+ .withQuery(
+ `query GetPost($id: ID!) {
post(id: $id) {
id
text
__typename
}
}`
- )
- .withVariables({
- id: '8f75eba5-6989-4dd3-b466-e464546ce374'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- errors: eachLike({
- message: like('An error occurred when fetching the post')
- })
- }
- });
- return await internals.provider.addInteraction(postQuery);
- });
-
- test('it returns the error', async () => {
-
- const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- post.subscribe((postValue) => {
-
- response = postValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{
- message: 'An error occurred when fetching the post'
- }]));
- });
- });
- });
+ )
+ .withVariables({
+ id: '8f75eba5-6989-4dd3-b466-e464546ce374'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ errors: eachLike({
+ message: like('An error occurred when fetching the post')
+ })
+ }
+ });
+ return await internals.provider.addInteraction(postQuery);
+ });
+
+ test('it returns the error', async () => {
+ const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ post.subscribe((postValue) => {
+ response = postValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error.graphQLErrors).toEqual(
+ expect.arrayContaining([
+ {
+ message: 'An error occurred when fetching the post'
+ }
+ ])
+ );
+ });
+ });
+ });
});
import { getTag } from './tags';
const internals = {
- provider: null
+ provider: null
};
describe('Tags store pact', () => {
-
- beforeAll(async () => {
-
- internals.provider = new Pact({
- port: 1234,
- dir: resolve(process.cwd(), 'pacts'),
- consumer: 'ForumClient',
- provider: 'ForumServer',
- pactfileWriteMode: 'update'
- });
-
- await internals.provider.setup();
- });
-
- afterEach(() => internals.provider.verify());
- afterAll(() => internals.provider.finalize());
-
- describe('When there\'s data', () => {
-
- describe('GetTag', () => {
-
- beforeAll(async () => {
-
- const tagQuery = new GraphQLInteraction()
- .given('there\'s data')
- .uponReceiving('a request to get a single tag')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTag')
- .withQuery(
- `query GetTag($id: ID!) {
+ beforeAll(async () => {
+ internals.provider = new Pact({
+ port: 1234,
+ dir: resolve(process.cwd(), 'pacts'),
+ consumer: 'ForumClient',
+ provider: 'ForumServer',
+ pactfileWriteMode: 'update'
+ });
+
+ await internals.provider.setup();
+ });
+
+ afterEach(() => internals.provider.verify());
+ afterAll(() => internals.provider.finalize());
+
+ describe("When there's data", () => {
+ describe('GetTag', () => {
+ beforeAll(async () => {
+ const tagQuery = new GraphQLInteraction()
+ .given("there's data")
+ .uponReceiving('a request to get a single tag')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTag')
+ .withQuery(
+ `query GetTag($id: ID!) {
tag(id: $id) {
id
topics {
__typename
}
}`
- )
- .withVariables({
- id: 'pineapple'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- tag: {
- id: like('pineapple'),
- topics: eachLike({
- id: like('cd038ae7-e8b4-4e38-9543-3d697e69ac34'),
- title: like('This topic is about pineapples'),
- updated_at: like(1619978944077),
- ttl: like(3555)
- })
- }
- }
- }
- });
- return await internals.provider.addInteraction(tagQuery);
- });
-
- test('it returns the tag', async () => {
-
- const tag = getTag('pineapple');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- tag.subscribe((tagValue) => {
-
- response = tagValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toEqual({
- id: 'pineapple',
- topics: [{
- id: 'cd038ae7-e8b4-4e38-9543-3d697e69ac34',
- title: 'This topic is about pineapples',
- updated_at: 1619978944077,
- ttl: 3555
- }]
- });
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s no data', () => {
-
- describe('GetTag', () => {
-
- beforeAll(async () => {
-
- const tagQuery = new GraphQLInteraction()
- .given('there\'s no data')
- .uponReceiving('a request to get a single tag')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTag')
- .withQuery(
- `query GetTag($id: ID!) {
+ )
+ .withVariables({
+ id: 'pineapple'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ tag: {
+ id: like('pineapple'),
+ topics: eachLike({
+ id: like('cd038ae7-e8b4-4e38-9543-3d697e69ac34'),
+ title: like('This topic is about pineapples'),
+ updated_at: like(1619978944077),
+ ttl: like(3555)
+ })
+ }
+ }
+ }
+ });
+ return await internals.provider.addInteraction(tagQuery);
+ });
+
+ test('it returns the tag', async () => {
+ const tag = getTag('pineapple');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ tag.subscribe((tagValue) => {
+ response = tagValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toEqual({
+ id: 'pineapple',
+ topics: [
+ {
+ id: 'cd038ae7-e8b4-4e38-9543-3d697e69ac34',
+ title: 'This topic is about pineapples',
+ updated_at: 1619978944077,
+ ttl: 3555
+ }
+ ]
+ });
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's no data", () => {
+ describe('GetTag', () => {
+ beforeAll(async () => {
+ const tagQuery = new GraphQLInteraction()
+ .given("there's no data")
+ .uponReceiving('a request to get a single tag')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTag')
+ .withQuery(
+ `query GetTag($id: ID!) {
tag(id: $id) {
id
topics {
__typename
}
}`
- )
- .withVariables({
- id: 'pineapple'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- tag: null
- }
- }
- });
- return await internals.provider.addInteraction(tagQuery);
- });
-
- test('it returns the tag', async () => {
-
- const tag = getTag('pineapple');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- tag.subscribe((tagValue) => {
-
- response = tagValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s a server error', () => {
-
- describe('GetTag', () => {
-
- beforeAll(async () => {
-
- const tagQuery = new GraphQLInteraction()
- .given('there\'s a server error')
- .uponReceiving('a request to get a single tag')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTag')
- .withQuery(
- `query GetTag($id: ID!) {
+ )
+ .withVariables({
+ id: 'pineapple'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ tag: null
+ }
+ }
+ });
+ return await internals.provider.addInteraction(tagQuery);
+ });
+
+ test('it returns the tag', async () => {
+ const tag = getTag('pineapple');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ tag.subscribe((tagValue) => {
+ response = tagValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's a server error", () => {
+ describe('GetTag', () => {
+ beforeAll(async () => {
+ const tagQuery = new GraphQLInteraction()
+ .given("there's a server error")
+ .uponReceiving('a request to get a single tag')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTag')
+ .withQuery(
+ `query GetTag($id: ID!) {
tag(id: $id) {
id
topics {
__typename
}
}`
- )
- .withVariables({
- id: 'pineapple'
- })
- .willRespondWith({
- status: 500
- });
- return await internals.provider.addInteraction(tagQuery);
- });
-
- test('it returns the error', async () => {
-
- const tag = getTag('pineapple');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- tag.subscribe((tagValue) => {
-
- response = tagValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBeInstanceOf(Error);
- });
- });
- });
-
- describe('When there\'s an error in the response', () => {
-
- describe('GetTag', () => {
-
- beforeAll(async () => {
-
- const tagQuery = new GraphQLInteraction()
- .given('there\'s an error in the response')
- .uponReceiving('a request to get a single tag')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTag')
- .withQuery(
- `query GetTag($id: ID!) {
+ )
+ .withVariables({
+ id: 'pineapple'
+ })
+ .willRespondWith({
+ status: 500
+ });
+ return await internals.provider.addInteraction(tagQuery);
+ });
+
+ test('it returns the error', async () => {
+ const tag = getTag('pineapple');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ tag.subscribe((tagValue) => {
+ response = tagValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBeInstanceOf(Error);
+ });
+ });
+ });
+
+ describe("When there's an error in the response", () => {
+ describe('GetTag', () => {
+ beforeAll(async () => {
+ const tagQuery = new GraphQLInteraction()
+ .given("there's an error in the response")
+ .uponReceiving('a request to get a single tag')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTag')
+ .withQuery(
+ `query GetTag($id: ID!) {
tag(id: $id) {
id
topics {
__typename
}
}`
- )
- .withVariables({
- id: 'pineapple'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- errors: eachLike({
- message: like('An error occurred when fetching the tag')
- })
- }
- });
- return await internals.provider.addInteraction(tagQuery);
- });
-
- test('it returns the error', async () => {
-
- const tag = getTag('pineapple');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- tag.subscribe((tagValue) => {
-
- response = tagValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{
- message: 'An error occurred when fetching the tag'
- }]));
- });
- });
- });
+ )
+ .withVariables({
+ id: 'pineapple'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ errors: eachLike({
+ message: like('An error occurred when fetching the tag')
+ })
+ }
+ });
+ return await internals.provider.addInteraction(tagQuery);
+ });
+
+ test('it returns the error', async () => {
+ const tag = getTag('pineapple');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ tag.subscribe((tagValue) => {
+ response = tagValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error.graphQLErrors).toEqual(
+ expect.arrayContaining([
+ {
+ message: 'An error occurred when fetching the tag'
+ }
+ ])
+ );
+ });
+ });
+ });
});
import { getTopic } from './topics';
const internals = {
- provider: null
+ provider: null
};
describe('Topics store pact', () => {
-
- beforeAll(async () => {
-
- internals.provider = new Pact({
- port: 1234,
- dir: resolve(process.cwd(), 'pacts'),
- consumer: 'ForumClient',
- provider: 'ForumServer',
- pactfileWriteMode: 'update'
- });
-
- await internals.provider.setup();
- });
-
- afterEach(() => internals.provider.verify());
- afterAll(() => internals.provider.finalize());
-
- describe('When there\'s data', () => {
-
- describe('GetTopic', () => {
-
- beforeAll(async () => {
-
- const topicQuery = new GraphQLInteraction()
- .given('there\'s data')
- .uponReceiving('a request to get a single topic')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTopic')
- .withQuery(
- `query GetTopic($id: ID!) {
+ beforeAll(async () => {
+ internals.provider = new Pact({
+ port: 1234,
+ dir: resolve(process.cwd(), 'pacts'),
+ consumer: 'ForumClient',
+ provider: 'ForumServer',
+ pactfileWriteMode: 'update'
+ });
+
+ await internals.provider.setup();
+ });
+
+ afterEach(() => internals.provider.verify());
+ afterAll(() => internals.provider.finalize());
+
+ describe("When there's data", () => {
+ describe('GetTopic', () => {
+ beforeAll(async () => {
+ const topicQuery = new GraphQLInteraction()
+ .given("there's data")
+ .uponReceiving('a request to get a single topic')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTopic')
+ .withQuery(
+ `query GetTopic($id: ID!) {
topic(id: $id) {
id
title
__typename
}
}`
- )
- .withVariables({
- id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- topic: {
- id: like('0b58959d-d448-4a4e-84b6-35e5ac0028d1'),
- title: like('The pacty topic of the day'),
- updated_at: like(1619979888906),
- ttl: like(3399),
- forum: {
- id: like('cucumber'),
- glyph: like('✽'),
- label: like('test_forums.cucumber')
- },
- tags: eachLike({
- id: like('skunk'),
- weight: like(44)
- }),
- posts: eachLike({
- id: like('ed93530e-6f9c-4701-91ef-14f9e0ed3e26'),
- text: like('The content of this post is very relevant'),
- created_at: like(1619979889798),
- author: like({
- id: like('07fb2ba0-0945-464a-b215-873296710c8c'),
- handle: like('cucumber_fan92')
- })
- })
- }
- }
- }
- });
- return await internals.provider.addInteraction(topicQuery);
- });
-
- test('it returns the topic', async () => {
-
- const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- topic.subscribe((topicValue) => {
-
- response = topicValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toEqual({
- id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1',
- title: 'The pacty topic of the day',
- updated_at: 1619979888906,
- ttl: 3399,
- forum: {
- id: 'cucumber',
- glyph: '✽',
- label: 'test_forums.cucumber'
- },
- tags: [{
- id: 'skunk',
- weight: 44
- }],
- posts: [{
- id: 'ed93530e-6f9c-4701-91ef-14f9e0ed3e26',
- text: 'The content of this post is very relevant',
- created_at: 1619979889798,
- author: {
- id: '07fb2ba0-0945-464a-b215-873296710c8c',
- handle: 'cucumber_fan92'
- }
- }]
- });
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s no data', () => {
-
- describe('GetTopic', () => {
-
- beforeAll(async () => {
-
- const topicQuery = new GraphQLInteraction()
- .given('there\'s no data')
- .uponReceiving('a request to get a single topic')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTopic')
- .withQuery(
- `query GetTopic($id: ID!) {
+ )
+ .withVariables({
+ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ topic: {
+ id: like('0b58959d-d448-4a4e-84b6-35e5ac0028d1'),
+ title: like('The pacty topic of the day'),
+ updated_at: like(1619979888906),
+ ttl: like(3399),
+ forum: {
+ id: like('cucumber'),
+ glyph: like('✽'),
+ label: like('test_forums.cucumber')
+ },
+ tags: eachLike({
+ id: like('skunk'),
+ weight: like(44)
+ }),
+ posts: eachLike({
+ id: like('ed93530e-6f9c-4701-91ef-14f9e0ed3e26'),
+ text: like('The content of this post is very relevant'),
+ created_at: like(1619979889798),
+ author: like({
+ id: like('07fb2ba0-0945-464a-b215-873296710c8c'),
+ handle: like('cucumber_fan92')
+ })
+ })
+ }
+ }
+ }
+ });
+ return await internals.provider.addInteraction(topicQuery);
+ });
+
+ test('it returns the topic', async () => {
+ const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ topic.subscribe((topicValue) => {
+ response = topicValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toEqual({
+ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1',
+ title: 'The pacty topic of the day',
+ updated_at: 1619979888906,
+ ttl: 3399,
+ forum: {
+ id: 'cucumber',
+ glyph: '✽',
+ label: 'test_forums.cucumber'
+ },
+ tags: [
+ {
+ id: 'skunk',
+ weight: 44
+ }
+ ],
+ posts: [
+ {
+ id: 'ed93530e-6f9c-4701-91ef-14f9e0ed3e26',
+ text: 'The content of this post is very relevant',
+ created_at: 1619979889798,
+ author: {
+ id: '07fb2ba0-0945-464a-b215-873296710c8c',
+ handle: 'cucumber_fan92'
+ }
+ }
+ ]
+ });
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's no data", () => {
+ describe('GetTopic', () => {
+ beforeAll(async () => {
+ const topicQuery = new GraphQLInteraction()
+ .given("there's no data")
+ .uponReceiving('a request to get a single topic')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTopic')
+ .withQuery(
+ `query GetTopic($id: ID!) {
topic(id: $id) {
id
title
__typename
}
}`
- )
- .withVariables({
- id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- data: {
- topic: null
- }
- }
- });
- return await internals.provider.addInteraction(topicQuery);
- });
-
- test('it returns the topic', async () => {
-
- const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- topic.subscribe((topicValue) => {
-
- response = topicValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBe(undefined);
- });
- });
- });
-
- describe('When there\'s a server error', () => {
-
- describe('GetTopic', () => {
-
- beforeAll(async () => {
-
- const topicQuery = new GraphQLInteraction()
- .given('there\'s a server error')
- .uponReceiving('a request to get a single topic')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTopic')
- .withQuery(
- `query GetTopic($id: ID!) {
+ )
+ .withVariables({
+ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ data: {
+ topic: null
+ }
+ }
+ });
+ return await internals.provider.addInteraction(topicQuery);
+ });
+
+ test('it returns the topic', async () => {
+ const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ topic.subscribe((topicValue) => {
+ response = topicValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBe(undefined);
+ });
+ });
+ });
+
+ describe("When there's a server error", () => {
+ describe('GetTopic', () => {
+ beforeAll(async () => {
+ const topicQuery = new GraphQLInteraction()
+ .given("there's a server error")
+ .uponReceiving('a request to get a single topic')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTopic')
+ .withQuery(
+ `query GetTopic($id: ID!) {
topic(id: $id) {
id
title
__typename
}
}`
- )
- .withVariables({
- id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
- })
- .willRespondWith({
- status: 500
- });
- return await internals.provider.addInteraction(topicQuery);
- });
-
- test('it returns the error', async () => {
-
- const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- topic.subscribe((topicValue) => {
-
- response = topicValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error).toBeInstanceOf(Error);
- });
- });
- });
-
- describe('When there\'s an error in the response', () => {
-
- describe('GetTopic', () => {
-
- beforeAll(async () => {
-
- const topicQuery = new GraphQLInteraction()
- .given('there\'s an error in the response')
- .uponReceiving('a request to get a single topic')
- .withRequest({
- path: '/graphql',
- method: 'POST'
- })
- .withOperation('GetTopic')
- .withQuery(
- `query GetTopic($id: ID!) {
+ )
+ .withVariables({
+ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
+ })
+ .willRespondWith({
+ status: 500
+ });
+ return await internals.provider.addInteraction(topicQuery);
+ });
+
+ test('it returns the error', async () => {
+ const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ topic.subscribe((topicValue) => {
+ response = topicValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error).toBeInstanceOf(Error);
+ });
+ });
+ });
+
+ describe("When there's an error in the response", () => {
+ describe('GetTopic', () => {
+ beforeAll(async () => {
+ const topicQuery = new GraphQLInteraction()
+ .given("there's an error in the response")
+ .uponReceiving('a request to get a single topic')
+ .withRequest({
+ path: '/graphql',
+ method: 'POST'
+ })
+ .withOperation('GetTopic')
+ .withQuery(
+ `query GetTopic($id: ID!) {
topic(id: $id) {
id
title
__typename
}
}`
- )
- .withVariables({
- id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
- })
- .willRespondWith({
- status: 200,
- headers: {
- 'Content-Type': 'application/json; charset=utf-8'
- },
- body: {
- errors: eachLike({
- message: like('An error occurred when fetching the topic')
- })
- }
- });
- return await internals.provider.addInteraction(topicQuery);
- });
-
- test('it returns the error', async () => {
-
- const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
- const { counter, promise: resolveAfterTwo } = resolveAfter(2);
- let response = null;
- topic.subscribe((topicValue) => {
-
- response = topicValue;
- counter();
- });
- expect(response.data).toBe(null);
- expect(response.loading).toBe(true);
- expect(response.error).toBe(undefined);
- await resolveAfterTwo;
- expect(response.data).toBe(null);
- expect(response.loading).toBe(false);
- expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{
- message: 'An error occurred when fetching the topic'
- }]));
- });
- });
- });
+ )
+ .withVariables({
+ id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1'
+ })
+ .willRespondWith({
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8'
+ },
+ body: {
+ errors: eachLike({
+ message: like('An error occurred when fetching the topic')
+ })
+ }
+ });
+ return await internals.provider.addInteraction(topicQuery);
+ });
+
+ test('it returns the error', async () => {
+ const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1');
+ const { counter, promise: resolveAfterTwo } = resolveAfter(2);
+ let response = null;
+ topic.subscribe((topicValue) => {
+ response = topicValue;
+ counter();
+ });
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(true);
+ expect(response.error).toBe(undefined);
+ await resolveAfterTwo;
+ expect(response.data).toBe(null);
+ expect(response.loading).toBe(false);
+ expect(response.error.graphQLErrors).toEqual(
+ expect.arrayContaining([
+ {
+ message: 'An error occurred when fetching the topic'
+ }
+ ])
+ );
+ });
+ });
+ });
});
import { store } from './apollo';
import { GET_TOPIC } from '$lib/data/queries';
-export const getTopic = (id: string) => store({ key: 'topic', query: GET_TOPIC, variables: { id } });
+export const getTopic = (id: string) =>
+ store({ key: 'topic', query: GET_TOPIC, variables: { id } });
import type { GlyphHash } from './glyph_hash';
type TestState = {
- glyphHash?: GlyphHash
+ glyphHash?: GlyphHash;
};
-
describe('Glyph Hash utility', () => {
-
- test('it throws an exception if the string is too short', () => {
-
- expect(() => {
-
- getGlyphHash('short');
- }).toThrow();
-
- expect(() => {
-
- getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
- }).toThrow();
-
- expect(() => {
-
- getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEF');
- }).not.toThrow();
- });
-
- test('it treats UUIDs with hyphens the same as those without', () => {
-
- const uuidWithHyphens = 'f7722355-2285-46c0-a55f-3483a826f3a6';
- const uuidWithoutHyphens = 'f7722355228546c0a55f3483a826f3a6';
-
- expect(getGlyphHash(uuidWithHyphens)).toEqual(getGlyphHash(uuidWithoutHyphens));
- });
-
- describe('it generates four sets of glyphs and colors', () => {
-
- const state: TestState = {};
-
- beforeEach(() => {
-
- state.glyphHash = getGlyphHash('f7722355-2285-46c0-a55f-3483a826f3a6');
- });
-
- test('there should be four glyph fragments', () => {
-
- expect(state.glyphHash.length).toBe(4);
- });
-
- test('each fragment should have a single character glyph', () => {
-
- for (const glyphHashFragment of state.glyphHash) {
- expect(typeof glyphHashFragment.glyph).toBe('string');
- expect(glyphHashFragment.glyph.length).toBe(1);
- }
- });
-
- test('each fragment should have a hexadecimal color', () => {
-
- for (const glyphHashFragment of state.glyphHash) {
- expect(typeof glyphHashFragment.color).toBe('string');
- expect(glyphHashFragment.color.length).toBe(7);
- expect(glyphHashFragment.color).toEqual(expect.stringMatching(/#[0-9a-f]{6}/));
- }
- });
- });
+ test('it throws an exception if the string is too short', () => {
+ expect(() => {
+ getGlyphHash('short');
+ }).toThrow();
+
+ expect(() => {
+ getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
+ }).toThrow();
+
+ expect(() => {
+ getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEF');
+ }).not.toThrow();
+ });
+
+ test('it treats UUIDs with hyphens the same as those without', () => {
+ const uuidWithHyphens = 'f7722355-2285-46c0-a55f-3483a826f3a6';
+ const uuidWithoutHyphens = 'f7722355228546c0a55f3483a826f3a6';
+
+ expect(getGlyphHash(uuidWithHyphens)).toEqual(getGlyphHash(uuidWithoutHyphens));
+ });
+
+ describe('it generates four sets of glyphs and colors', () => {
+ const state: TestState = {};
+
+ beforeEach(() => {
+ state.glyphHash = getGlyphHash('f7722355-2285-46c0-a55f-3483a826f3a6');
+ });
+
+ test('there should be four glyph fragments', () => {
+ expect(state.glyphHash.length).toBe(4);
+ });
+
+ test('each fragment should have a single character glyph', () => {
+ for (const glyphHashFragment of state.glyphHash) {
+ expect(typeof glyphHashFragment.glyph).toBe('string');
+ expect(glyphHashFragment.glyph.length).toBe(1);
+ }
+ });
+
+ test('each fragment should have a hexadecimal color', () => {
+ for (const glyphHashFragment of state.glyphHash) {
+ expect(typeof glyphHashFragment.color).toBe('string');
+ expect(glyphHashFragment.color.length).toBe(7);
+ expect(glyphHashFragment.color).toEqual(expect.stringMatching(/#[0-9a-f]{6}/));
+ }
+ });
+ });
});
export type GlyphHash = GlyphHashFragment[];
type GlyphHashFragment = {
- glyph: string,
- color: string
+ glyph: string;
+ color: string;
};
const internals = {
- kDehyphenRegex: /[-]/g,
- kSplitterRegex: /.{1,8}/g,
- kGlyphs: [
- '☽',
- '☆',
- '♢',
- '♡',
- '╱',
- '╲',
- '╳',
- '〰',
- '▷',
- '⏊',
- '〒',
- '▢',
- '◯',
- '⏃',
- '⏀',
- '⏆'
- ],
- unexpectedUUIDLength: class UnexpectedUUIDLength extends Error {
- name = 'UnexpectedUUIDLength';
- message = 'The provided string was not a valid UUIDv4, please provide a 32 character long string'
- }
+ kDehyphenRegex: /[-]/g,
+ kSplitterRegex: /.{1,8}/g,
+ kGlyphs: ['☽', '☆', '♢', '♡', '╱', '╲', '╳', '〰', '▷', '⏊', '〒', '▢', '◯', '⏃', '⏀', '⏆'],
+ unexpectedUUIDLength: class UnexpectedUUIDLength extends Error {
+ name = 'UnexpectedUUIDLength';
+ message =
+ 'The provided string was not a valid UUIDv4, please provide a 32 character long string';
+ }
};
// Return a glyph with color based on a 4 byte fragment of a UUIDv4
const getGlyphHashFragment = function (uuidFragment: string): GlyphHashFragment {
-
- const glyphIndex = parseInt(uuidFragment.substring(0, 2), 16) % 16;
- return {
- glyph: internals.kGlyphs[glyphIndex],
- color: `#${uuidFragment.substring(2, 8)}`
- };
+ const glyphIndex = parseInt(uuidFragment.substring(0, 2), 16) % 16;
+ return {
+ glyph: internals.kGlyphs[glyphIndex],
+ color: `#${uuidFragment.substring(2, 8)}`
+ };
};
// Return an array of glyphs based on a UUIDv4
export const getGlyphHash = function (uuid: string): GlyphHash {
+ const dehyphenedUuid = uuid.replace(/[-]/g, '');
- const dehyphenedUuid = uuid.replace(/[-]/g, '');
-
- if (dehyphenedUuid.length !== 32) {
- throw new internals.unexpectedUUIDLength();
- }
+ if (dehyphenedUuid.length !== 32) {
+ throw new internals.unexpectedUUIDLength();
+ }
- const hashFragments = dehyphenedUuid.match(internals.kSplitterRegex);
- return hashFragments.map(getGlyphHashFragment);
+ const hashFragments = dehyphenedUuid.match(internals.kSplitterRegex);
+ return hashFragments.map(getGlyphHashFragment);
};
import { readableTime } from './readable_time';
describe('readableTime', () => {
+ test('it shows negative time as 0', () => {
+ const response = readableTime(-1000);
- test('it shows negative time as 0', () => {
+ expect(response.count).toBe(0);
+ expect(response.label).toContain('seconds');
+ });
- const response = readableTime(-1000);
+ test('uses seconds as the smallest unit', () => {
+ const response = readableTime(10);
- expect(response.count).toBe(0);
- expect(response.label).toContain('seconds');
- });
+ expect(response.count).toBe(0);
+ expect(response.label).toContain('seconds');
+ });
- test('uses seconds as the smallest unit', () => {
+ test('correctly divides miliseconds into seconds', () => {
+ const response = readableTime(4000);
- const response = readableTime(10);
+ expect(response.count).toBe(4);
+ });
- expect(response.count).toBe(0);
- expect(response.label).toContain('seconds');
- });
+ test('uses seconds if the time is < 1 minute', () => {
+ const response = readableTime(59 * 1000);
- test('correctly divides miliseconds into seconds', () => {
+ expect(response.label).toContain('seconds');
+ });
- const response = readableTime(4000);
+ test('correctly divides miliseconds into minutes', () => {
+ const response = readableTime(2 * 60 * 1000);
- expect(response.count).toBe(4);
- });
+ expect(response.count).toBe(2);
+ });
- test('uses seconds if the time is < 1 minute', () => {
+ test('uses minutes if the time is < 1 hour', () => {
+ const response = readableTime(59 * 60 * 1000);
- const response = readableTime(59 * 1000);
+ expect(response.label).toContain('minutes');
+ });
- expect(response.label).toContain('seconds');
- });
+ test('correctly divides miliseconds into hours', () => {
+ const response = readableTime(2 * 60 * 60 * 1000);
- test('correctly divides miliseconds into minutes', () => {
+ expect(response.count).toBe(2);
+ });
- const response = readableTime(2 * 60 * 1000);
+ test('uses hours if the time is < 1 days', () => {
+ const response = readableTime(23 * 60 * 60 * 1000);
- expect(response.count).toBe(2);
- });
+ expect(response.label).toContain('hours');
+ });
- test('uses minutes if the time is < 1 hour', () => {
+ test('correctly divides miliseconds into days', () => {
+ const response = readableTime(2 * 24 * 60 * 60 * 1000);
- const response = readableTime(59 * 60 * 1000);
+ expect(response.count).toBe(2);
+ });
- expect(response.label).toContain('minutes');
- });
+ test('uses days if the time is >= 1 day', () => {
+ const response = readableTime(364 * 24 * 60 * 60 * 1000);
- test('correctly divides miliseconds into hours', () => {
+ expect(response.label).toContain('days');
+ });
- const response = readableTime(2 * 60 * 60 * 1000);
+ test('uses days as the maximum unit', () => {
+ const response = readableTime(Number.MAX_VALUE);
- expect(response.count).toBe(2);
- });
-
- test('uses hours if the time is < 1 days', () => {
-
- const response = readableTime(23 * 60 * 60 * 1000);
-
- expect(response.label).toContain('hours');
- });
-
- test('correctly divides miliseconds into days', () => {
-
- const response = readableTime(2 * 24 * 60 * 60 * 1000);
-
- expect(response.count).toBe(2);
- });
-
- test('uses days if the time is >= 1 day', () => {
-
- const response = readableTime(364 * 24 * 60 * 60 * 1000);
-
- expect(response.label).toContain('days');
- });
-
- test('uses days as the maximum unit', () => {
-
- const response = readableTime(Number.MAX_VALUE);
-
- expect(response.label).toContain('days');
- });
+ expect(response.label).toContain('days');
+ });
});
type DateMagnitude = 'day' | 'hour' | 'minute' | 'second';
type ReadableTime = {
- count: number,
- label: string
+ count: number;
+ label: string;
};
-
const internals = {
- magnitudes: {
- day: 86400000,
- hour: 3600000,
- minute: 60000,
- second: 1000
- },
- labels: {
- day: 'time.days',
- hour: 'time.hours',
- minute: 'time.minutes',
- second: 'time.seconds'
- },
-
- makeTimeReadable(time: number, magnitude: DateMagnitude): ReadableTime {
+ magnitudes: {
+ day: 86400000,
+ hour: 3600000,
+ minute: 60000,
+ second: 1000
+ },
+ labels: {
+ day: 'time.days',
+ hour: 'time.hours',
+ minute: 'time.minutes',
+ second: 'time.seconds'
+ },
- return {
- count: Math.floor(time / internals.magnitudes[magnitude]),
- label: internals.labels[magnitude]
- };
- }
+ makeTimeReadable(time: number, magnitude: DateMagnitude): ReadableTime {
+ return {
+ count: Math.floor(time / internals.magnitudes[magnitude]),
+ label: internals.labels[magnitude]
+ };
+ }
};
export const readableTime = function readableTime(time: number): ReadableTime {
-
- switch (true) {
- case time >= internals.magnitudes.day:
- return internals.makeTimeReadable(time, 'day');
- case time >= internals.magnitudes.hour:
- return internals.makeTimeReadable(time, 'hour');
- case time >= internals.magnitudes.minute:
- return internals.makeTimeReadable(time, 'minute');
- case time < 0:
- return internals.makeTimeReadable(0, 'second');
- default:
- return internals.makeTimeReadable(time, 'second');
- }
+ switch (true) {
+ case time >= internals.magnitudes.day:
+ return internals.makeTimeReadable(time, 'day');
+ case time >= internals.magnitudes.hour:
+ return internals.makeTimeReadable(time, 'hour');
+ case time >= internals.magnitudes.minute:
+ return internals.makeTimeReadable(time, 'minute');
+ case time < 0:
+ return internals.makeTimeReadable(0, 'second');
+ default:
+ return internals.makeTimeReadable(time, 'second');
+ }
};
import { resolveAfter } from './resolve_after';
describe('Resolve After', () => {
-
- test('it should throw if given 0', () => {
-
- expect(() => {
-
- resolveAfter(0);
- }).toThrow();
- });
-
- test('it should throw if given a negative number', () => {
-
- expect(() => {
-
- resolveAfter(-1);
- }).toThrow();
- });
-
- test('it should resolve after the specified number of times', () => {
-
- expect(() => {
-
- const { counter, promise: resolveAfterThree } = resolveAfter(3);
- let resolved = false;
-
- resolveAfterThree.then(() => (resolved = true));
- counter();
- expect(resolved).toBe(false);
- counter();
- expect(resolved).toBe(false);
- counter();
- expect(resolved).toBe(true);
- }).toThrow();
- });
+ test('it should throw if given 0', () => {
+ expect(() => {
+ resolveAfter(0);
+ }).toThrow();
+ });
+
+ test('it should throw if given a negative number', () => {
+ expect(() => {
+ resolveAfter(-1);
+ }).toThrow();
+ });
+
+ test('it should resolve after the specified number of times', () => {
+ expect(() => {
+ const { counter, promise: resolveAfterThree } = resolveAfter(3);
+ let resolved = false;
+
+ resolveAfterThree.then(() => (resolved = true));
+ counter();
+ expect(resolved).toBe(false);
+ counter();
+ expect(resolved).toBe(false);
+ counter();
+ expect(resolved).toBe(true);
+ }).toThrow();
+ });
});
export type ResolveAfterPromise = {
- counter: () => void,
- promise: Promise<void>
+ counter: () => void;
+ promise: Promise<void>;
};
export const resolveAfter = function (timesUntilResolve: number): ResolveAfterPromise {
+ let counter = null;
+ let currentValue = 0;
- let counter = null;
- let currentValue = 0;
+ if (timesUntilResolve <= 0) {
+ throw new Error('Resolve after requires a positive integer');
+ }
- if (timesUntilResolve <= 0) {
- throw new Error('Resolve after requires a positive integer');
- }
+ const promise: Promise<void> = new Promise((resolvePromise) => {
+ counter = () => {
+ if (++currentValue === timesUntilResolve) {
+ resolvePromise();
+ }
+ };
+ });
- const promise: Promise<void> = new Promise((resolvePromise) => {
-
- counter = () => {
-
- if (++currentValue === timesUntilResolve) {
- resolvePromise();
- }
- };
- });
-
- return {
- counter,
- promise
- };
+ return {
+ counter,
+ promise
+ };
};
-<script>
+<script lang="ts">
import InvalidRoute from '$lib/components/invalid_route/invalid_route.svelte';
</script>
-<script>
- import "../app.css";
+<script lang="ts">
+ import '../app.css';
import '$lib/i18n';
import { isLoading } from 'svelte-i18n';
<main>
<slot />
</main>
- <nav title={$_('forum_list.title')}>
- {#if $store.loading}
- <Loader />
- {/if}
- {#if $store.error}
- <ErrorBlock message={$_('forum_list.error.unavailable')} />
- {/if}
- {#if forums}
- <ForumList forums={forums} />
- {/if}
- </nav>
+ <nav title={$_('forum_list.title')}>
+ {#if $store.loading}
+ <Loader />
+ {/if}
+ {#if $store.error}
+ <ErrorBlock message={$_('forum_list.error.unavailable')} />
+ {/if}
+ {#if forums}
+ <ForumList {forums} />
+ {/if}
+ </nav>
<Footer />
{/if}
-<script context="module">
+<script lang="ts" context="module">
export const load = ({
- page: {
- params: { id }
- }
+ page: {
+ params: { id }
+ }
}) => ({ props: { id } });
</script>
-<script>
+<script lang="ts">
import Author from '$lib/components/author/author.svelte';
</script>
-<script context="module">
+<script lang="ts" context="module">
export const load = ({
- page: {
- params: { id }
- }
+ page: {
+ params: { id }
+ }
}) => ({ props: { id } });
</script>
-<script>
+<script lang="ts">
import { _ } from 'svelte-i18n';
import Forum from '$lib/components/forum/forum.svelte';
import ErrorBlock from '$lib/components/error_block/error_block.svelte';
-<script context="module">
+<script lang="ts" context="module">
export const load = ({
- page: {
- params: { id }
- }
+ page: {
+ params: { id }
+ }
}) => ({ props: { id } });
</script>
-<script>
+<script lang="ts">
import { _ } from 'svelte-i18n';
import { getTag } from '$lib/stores/tags';
import ErrorBlock from '$lib/components/error_block/error_block.svelte';
-<script>
+<script lang="ts">
import { _ } from 'svelte-i18n';
import Loader from '$lib/components/loader/loader.svelte';
</script>
-<script context="module">
+<script lang="ts" context="module">
export const load = ({
- page: {
- params: { id }
- }
+ page: {
+ params: { id }
+ }
}) => ({ props: { id } });
</script>
-<script>
+<script lang="ts">
import { _ } from 'svelte-i18n';
import { getPost } from '$lib/stores/posts';
import Post from '$lib/components/post/post.svelte';
-<script context="module">
+<script lang="ts" context="module">
export const load = ({
- page: {
- params: { id }
- }
+ page: {
+ params: { id }
+ }
}) => ({ props: { id } });
</script>
-<script>
+<script lang="ts">
import { onDestroy } from 'svelte';
import { _ } from 'svelte-i18n';
import { getTopic } from '$lib/stores/topics';
main {
- grid-column: col-start 2 / span 11;
+ grid-column: col-start 2 / span 11;
}
#forum {
- display: grid;
- font-family : 'ヒラギノ明朝 ProN' , 'Hiragino Mincho ProN' , '游明朝','游明朝体',YuMincho,'Yu Mincho' , 'MS 明朝' , 'MS Mincho' , HiraMinProN-W3 , 'TakaoEx明朝' , TakaoExMincho , 'MotoyaLCedar' , 'Droid Sans Japanese' , serif;
- grid-template-columns: repeat(12, [col-start] 1fr);
- grid-gap: 20px;
- grid-auto-rows: minmax(24px, auto);
+ display: grid;
+ font-family: 'ヒラギノ明朝 ProN', 'Hiragino Mincho ProN', '游明朝', '游明朝体', YuMincho,
+ 'Yu Mincho', 'MS 明朝', 'MS Mincho', HiraMinProN-W3, 'TakaoEx明朝', TakaoExMincho,
+ 'MotoyaLCedar', 'Droid Sans Japanese', serif;
+ grid-template-columns: repeat(12, [col-start] 1fr);
+ grid-gap: 20px;
+ grid-auto-rows: minmax(24px, auto);
}
body {
- background-color: whitesmoke;
+ background-color: whitesmoke;
}
-
{
- "name": "Forum",
- "start_url": "/",
- "background_color": "#ffffff",
- "display": "standalone",
- "theme_color": "#ffffff"
+ "name": "Forum",
+ "start_url": "/",
+ "background_color": "#ffffff",
+ "display": "standalone",
+ "theme_color": "#ffffff"
}
/** @type {import('@sveltejs/kit').Config} */
const config = {
- // Consult https://github.com/sveltejs/svelte-preprocess
- // for more information about preprocessors
- preprocess: preprocess(),
+ // Consult https://github.com/sveltejs/svelte-preprocess
+ // for more information about preprocessors
+ preprocess: preprocess(),
- kit: {
- adapter: adapter()
- }
+ kit: {
+ adapter: adapter()
+ }
};
export default config;
module.exports = {
- content: ['./src/**/*.{html,js,svelte,ts}'],
- plugins: [
- require('@tailwindcss/typography'), require('daisyui')
- ]
+ content: ['./src/**/*.{html,js,svelte,ts}'],
+ plugins: [require('@tailwindcss/typography'), require('daisyui')]
};
{
"extends": "./.svelte-kit/tsconfig.json",
- "compilerOptions": {
- "resolveJsonModule": true,
- "esModuleInterop": true
- }
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true
+ }
}