diff --git a/toolbox/fdc3-conformance/FDC3-1.2-Conformance-Test-Cases.md b/toolbox/fdc3-conformance/FDC3-1.2-Conformance-Test-Cases.md new file mode 100644 index 000000000..de8561864 --- /dev/null +++ b/toolbox/fdc3-conformance/FDC3-1.2-Conformance-Test-Cases.md @@ -0,0 +1,192 @@ +# FDC3 1.2 Conformance Test Cases + +## 1. Basic Tests + +_These are some basic sanity tests implemented in the FDC3 Conformance Framework. It is expected that Desktop Agent testers will run these first before commencing the much more thorough tests in section 2 onwards._ + +- `BasicCL1`: You can call the `fdc3.addContextListener` with the `fdc3.contact` context type. The returned listener object has an `unsubscribe` function. +- `BasicCL2`: You can call the `fdc3addContextListener` with no context type. The returned listener object has an `unsubscribe` function. +- `BasicIL1`: You can call the `fdc3.addIntentListener` on the `DesktopAgent` for an intent, and get back a `Listener` object with `unsubscribe` method. +- `BasicCH1`: A call to `fdc3.getCurrentChannel` on the `DesktopAgent` always returns a promise. +- `BasicCH2`: A call to `fdc3.getCurrentChannel()` returns a promise resolving to _null_ if called prior to any `joinChannel`. +- `BasicGI1`: A call to `fdc3.getInfo()` returns an object with `fdc3Version` and `provider` properties. +- `BasicAC1`: A call to `fdc3.getOrCreateChannel()` will return an promise resolving to an object matching the `Channel` interface, with properties of `id`, `type`, `broadcast`, `getCurrentContext` and `addContextListener`. +- `BasicUC1`: You can call the `fdc3.getSystemChannels()` function and receive a promise containing an array of more than 1 `Channel` objects, each with `type` and `id` set. +- `BasicJC1`: You can call `fdc3.joinChannel`, passing in the `id` of one of the system channels. After the returned promise is resolved, `fdc3.getCurrentChannel` should then return that joined channel. +- `BasicJC2`: You can call `fdc3.joinChannel`, passing in the `id` of one of the system channels. `fdc3.getCurrentChannel()` will return the same channel back. +- `BasicLC1`: You can call `fdc3.leaveCurrentChannel` at any time without it throwing an exception. +- `BasicRI1`: You can call `fdc3.raiseIntentForContext`, passing in a context object with some `type` field. + +## 2. System / User Channels + +### User Channels Broadcast (Basic) + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. addContextListener |Call `fdc3.addContextListener(null, handler)`
Check listener object returned
Check that there is an `unsubscribe` function on the returned object | +| A | 2. joinChannel |`fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel | +| B | 3. joinChannel | `fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel | +| B | 4. Broadcast | `fdc3.broadcast()` | +| A | 5. Receive Context | Instrument object matches the one broadcast in 4 above. | + +- `UC Basic Usage 1` Perform above test +- `UC Basic Usage 2` Perform above test, but join channel first and then `fdc3.addContextListener()` +- `UC Basic Usage 3` Do the app B steps first to populate the channel with context, check that A will receive the context after joining + +### User Channels Broadcast (Filtered Context) + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. addContextListener | Call `fdc3.addContextListener("fdc3.instrument", handler)`
Check listener object returned
Check that there is an `unsubscribe` function on the returned object | +| A | 2. joinChannel | `fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel | +| B | 3. joinChannel | `fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel | +| B | 4. Broadcast | `fdc3.broadcast()` the instrument context.
`fdc3.broadcast()` a contact context. | +| A | 5. Receive Context | Instrument object matches the one broadcast in 4 above.
Check that the contact is not received. | + +- `UCFilteredContext1`: Perform above test + +| App | Step | Details | +|-----|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. addContextListener | Call `addContextListener (“fdc3.instrument”, handler)`
Check listener object returned
Check that there is an unsubscribe function on the returned object
Call `addContextListener (“fdc3.contact”, handler)`
Check listener object returned
Check that there is an unsubscribe function on the returned object | +| A | 2. joinChannel | `fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel. Check that there is an unsubscribe function on the returned object | +| B | 3. joinChannel | `fdc3.getSystemChannels()`
Check channels are returned.
Call `fdc3.joinChannel()` on first non-global channel | +| B | 4. Broadcast | `fdc3.broadcast()` the instrument context.
`fdc3.broadcast()` a contact context. | +| A | 5. Receive Context | Instrument object matches the one broadcast in 4 above.
Contact object matches the one broadcast in 4 above. | + +- `UCFilteredContext2`: Perform above test +- `UCFilteredContext3`: Perform above test, except joining a _different_ channel. Check that you _don't_ receive anything. +- `UCUnsubscribe`: Perform above test, except that after joining, **A** then `unsubscribe()`s the context listener. Check that **A** _doesn't_ receive anything. +- `UCFilteredContext4`: Perform above test, except that after joining, **A** changes channel with a further _different_ channel. Check that **A** _doesn't_ receive anything. + +## 3. App Channels + +### App Channels Broadcast (Basic) + + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. createChannel |`const testChannel = await fdc3.getOrCreateChannel("test-channel")` | +| A | 2. addContextListener |Call `testChannel.addContextListener(null, handler)`
Check listener object returned
Check that there is an `unsubscribe` function on the returned object | +| B | 3. createChannel | `const testChannel = fdc3.getOrCreateChannel("test-channel")` | +| B | 4. Broadcast | `testChannel.broadcast()` | +| A | 5. Receive Context | Instrument object matches the one broadcast in 4 above. | + +- `ACBasicUsage1` Perform above test + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| B | 1. createChannel | `const testChannel = await fdc3.getOrCreateChannel("test-channel")` | +| B | 2. Broadcast | `testChannel.broadcast()` | +| A | 3. createChannel |`const testChannel = await fdc3.getOrCreateChannel("test-channel")` | +| A | 4. getCurrentContext |Call `testChannel.getCurrentContext()`
Check that the Context object returned is of the expected type. | + +- `ACBasicUsage2` Perform above test. + +### App Channels Broadcast (Filtered Context) + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. createChannel |`const testChannel = await fdc3.getOrCreateChannel("test-channel")` | +| A | 2. addContextListener | Call `testChannel.addContextListener("fdc3.instrument", handler)`
Check listener object returned
Check that there is an `unsubscribe` function on the returned object | +| B | 3. createChannel | `const testChannel = await fdc3. getOrCreateChannel("test-channel")` | +| B | 4. Broadcast | `testChannel.broadcast()` the instrument context.
`testChannel.broadcast()` a contact context. | +| A | 5. Receive Context | Instrument object matches the one broadcast in 4 above.
Check that the contact is not received. | + +- `ACFilteredContext1`: Perform above test +- `ACFilteredContext2`: Perform above test, but add listeners for both `fdc3.instrument` and `fdc3.contact` in `addContextListener` step. Both should be received. +- `ACFilteredContext3`: Perform above test, except creating a _different_ channel in app B. Check that you _don't_ receive anything (as the channels don't match). +- `ACUnsubscribe`: Perform above test, except that after creating the channel **A** then `unsubscribe()`s the listener it added to the channel. Check that **A** _doesn't_ receive anything. +- `ACFilteredContext4`: Perform above test, except that after creating the channel **A** creates another channel with a further _different_ channel id and adds a further context listener to it. Check that **A** is still able to receive context on the first channel (i.e. it is unaffected by the additional channel) and does *NOT* receive anything on the second channel. + +### App Channel History + +| App | Step | Details | +|-----|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | 1. createChannel |`fdc3.getOrCreateChannel("test-channel")` | +| B | 2. createChannel | `fdc3.getOrCreateChannel("test-channel")` | +| B | 3. Broadcast | `testChannel.broadcast()` the instrument context.
`testChannel.broadcast()` a contact context. | +| A | 4. Receive Context | `testChannel.getCurrentContext('fdc3.instrument')` returns the last broadcast instrument
`testChannel.getCurrentContext('fdc3.contact')` returns the last broadcast contact | + +- `ACContextHistoryTyped`: Perform above test. +- `ACContextHistoryMultiple`: **B** Broadcasts multiple history items of both types. Only the last version of each type is received by **A**. +- `ACContextHistoryLast`: **A** calls `testChannel.getCurrentContext()` retrieves the last broadcast context item + +## 4. Open API + +### A Opens B + +- `AOpensB1`: **A** calls `fdc3.open(‘app B Name’)`, check app **B** opens +- `AOpensB2`: **A** calls `fdc3.open({name: “”})`, check app **B** opens +- `AOpensB3`: **A** calls `fdc3.open({name: “”, appId: “”})`, check app **B** opens + +### A Fails To Open B + +- `AFailsToOpenB1-3`: Run the above 4 tests again with a non-existent app name/app id. Should return “App Not Found” Error from https://fdc3.finos.org/docs/api/ref/Errors#openerror + +### A Opens B With Context + +| App | Step | Description | +|-----|-----------------|----------------------------------------------------------| +| A | 1. Opening App | various open methods as in `AOpensB1-3` except with a `` argument of type `fdc3.testReceiver`
check app opens | +| B | 2. Context present | `fdc3.addContextListener()`
- receives `` from **A** | + +- `AOpensB1WithContext1`: **A** calls `fdc3.open(‘app B Name', ctx)`, check app **B** opens +- `AOpensB2WithContext2`: **A** calls `fdc3.open({name: “”}, ctx)`, check app **B** opens +- `AOpensB3WithContext3`: **A** calls `fdc3.open({name: “”, appId: “”}, ctx)`, check app **B** opens +- `AOpensBWithSpecificContext1`: Perform `AOpensB1WithContext1` above but replace **B**s call with `fdc3.addContextListener('fdc3.testReceiver`)` + + +### Specific Context + + +| App | Step | Description | +|-----|-----------------|-------------------------------------------------------------------------------------------------------------------------------| +| A | 1. Opening App | `fdc3.open(‘app Name’, )`
check app opens | +| B | 2. Context present | `fdc3.addContextListener()`
`fdc3.addContextListener('fdc3.instrument')`
- receives from A | +| A | 3. Promise | - receives a rejection from the open promise with “App Timeout’ from
https://fdc3.finos.org/docs/api/ref/Errors#openerror | + +- `AOpensBMultipleListen`: The correct (first) context listener should receive the context, and the promise resolves successfully in **A**. +- `AOpensBMalformedContext`: **A** tries to pass malformed context to **B** (i.e. no `type` field on the context object). Context listeners receive nothing, promise completes successfully. + +## 5. Intents + +### Setup + +You will need to pre-populate the AppDirectory with the following items: + +| App | Required Metadata | +|-----|------------------------------------------------------------------------------------------------------------------------------------------------------| +| A | A’s AppD Record contains: `aTestingIntent` (with context type `testContextX`, `testContextZ`) and `sharedTestingIntent1` (with context type `testContextX`) | +| B | B’s AppD Record contains `bTestingIntent` (with context type `testContextY`) and `sharedTestingIntent1` (with context types `testContextX` and `testContextY`) | +| C | C’s AppD Record contains `cTestingIntent` (with context type `testContextX`) | + +Also we assume a fourth app **D** that is going to discover the intents in the other 3. + +### Find Intent From AppD + +- `IntentAppD`: Calls `fdc3.findIntent(‘aTestingIntent’)`. Receives promise containing an appIntent with metadata containing `aTestingIntent` and only **A** app metadata. +- `WrongIntentAppD`: Calls `fdc3.findIntent(‘nonExistentIntent’)`. Rejects with no apps found error https://fdc3.finos.org/docs/api/ref/Errors#resolveerror +- `IntentAppDRightContext`: Calls `fdc3.findIntent(‘aTestingIntent’, ‘fdc3.testContextX’)`. Receives promise containing an appIntent with metadata containing `aTestingIntent` and only **A** app metadata. +- `IntentAppDWrongContext`: Calls `fdc3.findIntent(‘aTestingIntent’, ‘fdc3.testContextY’)`. Rejects with no apps found error https://fdc3.finos.org/docs/api/ref/Errors#resolveerror +- `IntentAppDMultiple1`: Calls `fdc3.findIntent(‘sharedTestingIntent1’)`. Receives promise containing an appIntent with metadata containing `sharedTestingIntent` and only **A** and **B** app metadata. +- `IntentAppDMultiple2`: Calls `fdc3.findIntent(‘sharedTestingIntent1’, 'testContextX')`. Receives promise containing an appIntent with metadata containing `sharedTestingIntent` and only **A** and **B** app metadata. +- `IntentAppDMultiple3`: Calls `fdc3.findIntent(‘sharedTestingIntent1’, 'testContextY')`. Receives promise containing an appIntent with metadata containing `sharedTestingIntent` and only **B** app metadata. + +### Find Intents By Context + +- `SingleContext`: Call `fdc3.findIntentsByContext(‘fdc3.testContextX’)`. Should return `aTestingIntent` (app **A**), `sharedTestingIntent1` (**A**, **B**) and `cTestingIntent` (**C**) AND nothing else. +- `NoContext`: Call `fdc3.findIntentsByContext()`. Throws error of `NoAppsFound` + +### Raise Intent + +| App | Step | Details | +|-----|----------------|---------------------------------------------------------------------------------------------------| +| A | 1. Raise | `fdc3.raiseIntent(‘sharedTestingIntent1’, {testContextY})`
starts app B. | +| B | 2. Gather Context | `fdc.addIntentListener(‘sharedTestingIntent1’)`
Receives testContextY, matching that sent by D | + +- `SingleResolve1`: Perform above test +- `TargetedResolve1`: Use `fdc3.raiseIntent(‘aTestingIntent’, {testContextX}, )` to start app A, otherwise, as above +- `TargetedResolve2`: Use `fdc3.raiseIntent(‘aTestingIntent’, {testContextX}, {name: ""})` to start app A, otherwise, as above +- `TargetedResolve3`: Use `fdc3.raiseIntent(‘aTestingIntent’, {testContextX}, {name: “”, appId: “”})` to start app A, otherwise, as above +- `FailedResolve1-3` As with `TargetedResolve1-3`, but use `fdc3.raiseIntent(‘aTestingIntent’, {testContextY}, )` and variations. You will receive `NoAppsFound` Error +- `FailedResolve4` As above, but use `fdc3.raiseIntent(‘aTestingIntent’, {testContextX}, )`. You will receive `NoAppsFound` Error diff --git a/toolbox/fdc3-conformance/README.md b/toolbox/fdc3-conformance/README.md new file mode 100644 index 000000000..163223878 --- /dev/null +++ b/toolbox/fdc3-conformance/README.md @@ -0,0 +1,8 @@ +# FDC3 Conformance Testing + +This folder contains test packs (test definitions) for conformance with FDC3 1.2 and 2.0. + +You can find the implementation of these tests in the [FDC3 Conformance Framework](https://github.com/finos/FDC3-conformance-framework) project. + +- [FDC3 1.2 Conformance Pack](FDC3-1.2-Conformance-Test-Cases.md) +- FDC3 2.0 Conformance Pack (tbd) diff --git a/website/data/community.json b/website/data/community.json index ae74ba23b..eddb5920e 100644 --- a/website/data/community.json +++ b/website/data/community.json @@ -77,6 +77,16 @@ "badges": [], "description": "

When developing an FDC3-compliant app or desktop agent, you need to test. Because FDC3 is about communicating, you need at least one other app to communicate with. You could grab an existing app, but there may not be one available that uses the messaging you need to test.

Many developers end up writing their own helper tool that they discard when their app is done. So many devs have created these throwaway apps that the Finsemble team @ Cosaic decided to build and contribute a workbench (for any FDC3-compliant platform) that helps develop and test your app without writing throwaway code.

" }, + { + "title": "FDC3 Conformance Framework", + "publisher": "FDC3 / FINOS / ScottLogic", + "image": "/img/community/conformance.png", + "infoLink": "https://github.com/finos/FDC3-conformance-framework", + "docsLink": "https://github.com/finos/FDC3/blob/master/toolbox/fdc3-conformance/README.md", + "type": "examples-and-training", + "badges": [], + "description": "

As part of FINOS' commitment to the FDC3 standard, ScottLogic have been engaged to produce a conformance testing framework for desktop agent API compatibility. Delivered as a set of FDC3 Applications, this Conformance pack will test 1.2 and 2.0 compatibility by exercising a desktop agent's implementation of the FDC3 API.

" + }, { "title": "FDC3 eXplained", "publisher": "FDC3 / FINOS", diff --git a/website/static/img/community/conformance.png b/website/static/img/community/conformance.png new file mode 100644 index 000000000..643c88139 Binary files /dev/null and b/website/static/img/community/conformance.png differ