From 273f13332188bc8b7f1f3c960a4409fae8b3da06 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Mon, 20 Nov 2023 09:18:24 -0300 Subject: [PATCH] Replace the existing plugins mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A summary of the discussion on #1492: - the modular API should be the only way to pass optional functionality to the SDK - this means we need to replace the existing ClientOptions.plugins mechanism, which is currently used to pass a Vcdiff decoder - since the modular variant of the SDK only exists for web at the moment, we will bundle Vcdiff decoding into all other platforms (in which bundle size is not much of a concern) - on web, if you want deltas, you have to use the modular variant of the SDK So, we remove the ClientOptions.plugins mechanism and introduce a tree-shakable Vcdiff module. Note that this means that, currently, it is no longer possible to use deltas inside a Web Worker. We’ll address this in #1514. The README example of configuring a channel to use deltas is copied from the README of the vcdiff-decoder library. (Once ably-js v2 is released, we should update the instructions in the vcdiff-decoder library’s README to make it clear they only apply to v1. I’ve raised #1513 for this.) --- README.md | 70 +++- ably.d.ts | 10 - modules.d.ts | 19 + scripts/moduleReport.ts | 1 + src/common/lib/client/baserealtime.ts | 5 +- src/common/lib/client/modulesmap.ts | 2 + src/common/lib/client/realtimechannel.ts | 2 +- src/common/lib/types/message.ts | 19 +- src/common/platform.ts | 7 + src/platform/nativescript/index.ts | 2 + src/platform/nodejs/index.ts | 2 + src/platform/react-native/index.ts | 2 + src/platform/web/index.ts | 5 + src/platform/web/modules.ts | 2 + src/platform/web/modules/vcdiff.ts | 1 + test/browser/modules.test.js | 38 +- test/common/globals/named_dependencies.js | 4 - test/realtime/delta.test.js | 28 +- test/realtime/shared/delta_tests.js | 418 +++++++++++----------- test/support/browser_file_list.js | 1 - test/support/browser_setup.js | 3 - 21 files changed, 393 insertions(+), 248 deletions(-) create mode 100644 src/platform/web/modules/vcdiff.ts diff --git a/README.md b/README.md index cabecb613c..b0f70de5b2 100644 --- a/README.md +++ b/README.md @@ -188,12 +188,73 @@ channel.subscribe('myEvent', function (message) { Subscribing to a channel in delta mode enables [delta compression](https://www.ably.com/docs/realtime/channels/channel-parameters/deltas). This is a way for a client to subscribe to a channel so that message payloads sent contain only the difference (ie the delta) between the present message and the previous message on the channel. -Configuring a channel for deltas is detailed in the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage). +To subscribe to a channel in delta mode, you must: + +1. Create a client that supports deltas (this only applies when running in a browser); +2. Configure the channel to operate in delta mode. + +#### Creating a client that supports deltas + +This section only applies when running in a browser. The Realtime client on all other platforms includes delta support. + +To use delta functionality in the browser, you must use the [modular variant of the library](#modular-tree-shakable-variant) and create a client that includes the `Vcdiff` module: + + ```javascript + import { BaseRealtime, WebSocketTransport, FetchRequest, Vcdiff } from 'ably/modules'; + + const options = { key: 'YOUR_ABLY_KEY' }; + const client = new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + Vcdiff + }); + ``` + +#### Configuring a channel to operate in delta mode + +To configure a channel to operate in delta mode, specify channel parameters of `{ delta: 'vcdiff' }` when fetching the channel: + +```javascript +const channel = realtime.channels.get('your-ably-channel', { + params: { + delta: 'vcdiff' + } +}); +``` Beyond specifying channel options, the rest is transparent and requires no further changes to your application. The `message.data` instances that are delivered to your listening function continue to contain the values that were originally published. If you would like to inspect the `Message` instances in order to identify whether the `data` they present was rendered from a delta message from Ably then you can see if `extras.delta.format` equals `'vcdiff'`. +## Delta Plugin + +From version 1.2 this client library supports subscription to a stream of Vcdiff formatted delta messages from the Ably service. For certain applications this can bring significant data efficiency savings. +This is an optional feature so our + +See the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage) for setup and usage examples. + + +```javascript +const Ably = require('ably'); +const vcdiffPlugin = require('@ably/vcdiff-decoder'); + +const realtime = new Ably.Realtime({ + key: 'YOUR_ABLY_KEY', + plugins: { + vcdiff: vcdiffPlugin + }, + log: { level: 4 } // optional +}); + +const channel = realtime.channels.get('your-ably-channel', { + params: { + delta: 'vcdiff' + } +}); + +channel.subscribe(msg => console.log("Received message: ", msg)); +``` + ### Publishing to a channel ```javascript @@ -454,13 +515,6 @@ const nextPage = await statsPage.next(); // retrieves the next page as Pa const time = await client.time(); // time is in ms since epoch ``` -## Delta Plugin - -From version 1.2 this client library supports subscription to a stream of Vcdiff formatted delta messages from the Ably service. For certain applications this can bring significant data efficiency savings. -This is an optional feature so our - -See the [@ably-forks/vcdiff-decoder documentation](https://github.com/ably-forks/vcdiff-decoder#usage) for setup and usage examples. - ## Support, feedback and troubleshooting Please visit http://support.ably.com/ for access to our knowledgebase and to ask for any assistance. diff --git a/ably.d.ts b/ably.d.ts index ffaf96a8ee..bd7a7a5dac 100644 --- a/ably.d.ts +++ b/ably.d.ts @@ -565,16 +565,6 @@ declare namespace Types { * @defaultValue 10s */ realtimeRequestTimeout?: number; - - /** - * A map between a plugin type and a plugin object. - */ - plugins?: { - /** - * A plugin capable of decoding `vcdiff`-encoded messages. For more information on how to configure a channel to use delta encoding, see the [documentation for the `@ably-forks/vcdiff-decoder` package](https://github.com/ably-forks/vcdiff-decoder#usage). - */ - vcdiff?: any; - }; } /** diff --git a/modules.d.ts b/modules.d.ts index c7fa8a0240..31c2e7891b 100644 --- a/modules.d.ts +++ b/modules.d.ts @@ -174,6 +174,20 @@ export declare const MessageInteractions: unknown; */ export declare const RealtimePublishing: unknown; +/** + * Provides a {@link BaseRealtime} instance with the ability to use [delta compression](https://www.ably.com/docs/realtime/channels/channel-parameters/deltas). + * + * To create a client that includes this module, include it in the `ModulesMap` that you pass to the {@link BaseRealtime.constructor}: + * + * ```javascript + * import { BaseRealtime, WebSocketTransport, FetchRequest, Vcdiff } from 'ably/modules'; + * const realtime = new BaseRealtime(options, { WebSocketTransport, FetchRequest, Vcdiff }); + * ``` + * + * For information on how to configure a channel to use delta encoding, see [the documentation in the `README`](https://github.com/ably/ably-js/blob/main/README.md#configuring-a-channel-to-operate-in-delta-mode). + */ +export declare const Vcdiff: unknown; + /** * Pass a `ModulesMap` to { @link BaseRest.constructor | the constructor of BaseRest } or {@link BaseRealtime.constructor | that of BaseRealtime} to specify which functionality should be made available to that client. */ @@ -232,6 +246,11 @@ export interface ModulesMap { * See {@link RealtimePublishing | documentation for the `RealtimePublishing` module}. */ RealtimePublishing?: typeof RealtimePublishing; + + /** + * See {@link Vcdiff | documentation for the `Vcdiff` module}. + */ + Vcdiff?: typeof Vcdiff; } /** diff --git a/scripts/moduleReport.ts b/scripts/moduleReport.ts index 4b9aec1738..c5cc7d4941 100644 --- a/scripts/moduleReport.ts +++ b/scripts/moduleReport.ts @@ -18,6 +18,7 @@ const moduleNames = [ 'FetchRequest', 'MessageInteractions', 'RealtimePublishing', + 'Vcdiff', ]; // List of all free-standing functions exported by the library along with the diff --git a/src/common/lib/client/baserealtime.ts b/src/common/lib/client/baserealtime.ts index 6e4cd67058..18bfa15299 100644 --- a/src/common/lib/client/baserealtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -11,8 +11,9 @@ import ClientOptions from '../../types/ClientOptions'; import * as API from '../../../../ably'; import { ModulesMap, RealtimePresenceModule } from './modulesmap'; import { TransportNames } from 'common/constants/TransportName'; -import { TransportImplementations } from 'common/platform'; +import Platform, { TransportImplementations } from 'common/platform'; import { RealtimePublishing } from './realtimepublishing'; +import { VcdiffDecoder } from '../types/message'; /** `BaseRealtime` is an export of the tree-shakable version of the SDK, and acts as the base class for the `DefaultRealtime` class exported by the non tree-shakable version. @@ -20,6 +21,7 @@ import { RealtimePublishing } from './realtimepublishing'; class BaseRealtime extends BaseClient { readonly _RealtimePresence: RealtimePresenceModule | null; readonly __RealtimePublishing: typeof RealtimePublishing | null; + readonly _decodeVcdiff: VcdiffDecoder | null; // Extra transport implementations available to this client, in addition to those in Platform.Transports.bundledImplementations readonly _additionalTransportImplementations: TransportImplementations; _channels: any; @@ -31,6 +33,7 @@ class BaseRealtime extends BaseClient { this._additionalTransportImplementations = BaseRealtime.transportImplementationsFromModules(modules); this._RealtimePresence = modules.RealtimePresence ?? null; this.__RealtimePublishing = modules.RealtimePublishing ?? null; + this._decodeVcdiff = (modules.Vcdiff ?? (Platform.Vcdiff.supported && Platform.Vcdiff.bundledDecode)) || null; this.connection = new Connection(this, this.options); this._channels = new Channels(this); if (options.autoConnect !== false) this.connect(); diff --git a/src/common/lib/client/modulesmap.ts b/src/common/lib/client/modulesmap.ts index 17d1a9e6ad..e2bb6acfba 100644 --- a/src/common/lib/client/modulesmap.ts +++ b/src/common/lib/client/modulesmap.ts @@ -11,6 +11,7 @@ import { fromValues as presenceMessageFromValues, fromValuesArray as presenceMessagesFromValuesArray, } from '../types/presencemessage'; +import { VcdiffDecoder } from '../types/message'; export interface PresenceMessageModule { presenceMessageFromValues: typeof presenceMessageFromValues; @@ -33,6 +34,7 @@ export interface ModulesMap { FetchRequest?: typeof fetchRequest; MessageInteractions?: typeof FilteredSubscriptions; RealtimePublishing?: typeof RealtimePublishing; + Vcdiff?: VcdiffDecoder; } export const allCommonModules: ModulesMap = { Rest }; diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index 01577837fb..eec3b1bdcc 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -114,7 +114,7 @@ class RealtimeChannel extends EventEmitter { this._attachResume = false; this._decodingContext = { channelOptions: this.channelOptions, - plugins: client.options.plugins || {}, + decodeVcdiff: client._decodeVcdiff ?? undefined, baseEncodedPreviousPayload: undefined, }; this._lastPayload = { diff --git a/src/common/lib/types/message.ts b/src/common/lib/types/message.ts index 1f149772ed..aafba56ca9 100644 --- a/src/common/lib/types/message.ts +++ b/src/common/lib/types/message.ts @@ -22,13 +22,11 @@ export type CipherOptions = { }; }; +export type VcdiffDecoder = (delta: Uint8Array, source: Uint8Array) => Uint8Array; + export type EncodingDecodingContext = { channelOptions: ChannelOptions; - plugins: { - vcdiff?: { - decode: (delta: Uint8Array, source: Uint8Array) => Uint8Array; - }; - }; + decodeVcdiff?: VcdiffDecoder; baseEncodedPreviousPayload?: Buffer | BrowserBufferlike; }; @@ -36,7 +34,6 @@ function normaliseContext(context: CipherOptions | EncodingDecodingContext | Cha if (!context || !(context as EncodingDecodingContext).channelOptions) { return { channelOptions: context as ChannelOptions, - plugins: {}, baseEncodedPreviousPayload: undefined, }; } @@ -216,8 +213,12 @@ export async function decode( throw new Error('Unable to decrypt message; not an encrypted channel'); } case 'vcdiff': - if (!context.plugins || !context.plugins.vcdiff) { - throw new ErrorInfo('Missing Vcdiff decoder (https://github.com/ably-forks/vcdiff-decoder)', 40019, 400); + if (!context.decodeVcdiff) { + if (Platform.Vcdiff.supported) { + Utils.throwMissingModuleError('Vcdiff'); + } else { + throw new ErrorInfo(Platform.Vcdiff.errorMessage, 40019, 400); + } } if (typeof Uint8Array === 'undefined') { throw new ErrorInfo( @@ -236,7 +237,7 @@ export async function decode( const deltaBaseBuffer = Platform.BufferUtils.toBuffer(deltaBase as Buffer); data = Platform.BufferUtils.toBuffer(data); - data = Platform.BufferUtils.arrayBufferViewToBuffer(context.plugins.vcdiff.decode(data, deltaBaseBuffer)); + data = Platform.BufferUtils.arrayBufferViewToBuffer(context.decodeVcdiff(data, deltaBaseBuffer)); lastPayload = data; } catch (e) { throw new ErrorInfo('Vcdiff delta decode failed with ' + e, 40018, 400); diff --git a/src/common/platform.ts b/src/common/platform.ts index 6d5a6245bf..bfd1c5fd64 100644 --- a/src/common/platform.ts +++ b/src/common/platform.ts @@ -8,6 +8,7 @@ import * as WebBufferUtils from '../platform/web/lib/util/bufferutils'; import * as NodeBufferUtils from '../platform/nodejs/lib/util/bufferutils'; import { IUntypedCryptoStatic } from '../common/types/ICryptoStatic'; import TransportName from './constants/TransportName'; +import { VcdiffDecoder } from './lib/types/message'; type Bufferlike = WebBufferUtils.Bufferlike | NodeBufferUtils.Bufferlike; type BufferUtilsOutput = WebBufferUtils.Output | NodeBufferUtils.Output; @@ -39,4 +40,10 @@ export default class Platform { }; static Defaults: IDefaults; static WebStorage: IWebStorage | null; + static Vcdiff: + | { supported: false; errorMessage: string /* explains why this platform does not support vcdiff */ } + | { + supported: true; + bundledDecode: VcdiffDecoder | null /* { supported: true, bundledDecode: null } means that the decode implementation can be provided via ModulesMap */; + }; } diff --git a/src/platform/nativescript/index.ts b/src/platform/nativescript/index.ts index 119fdcb048..baa9524006 100644 --- a/src/platform/nativescript/index.ts +++ b/src/platform/nativescript/index.ts @@ -3,6 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest'; import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -29,6 +30,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/nodejs/index.ts b/src/platform/nodejs/index.ts index b066225b6b..dfe59ca6ec 100644 --- a/src/platform/nodejs/index.ts +++ b/src/platform/nodejs/index.ts @@ -3,6 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest'; import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from './lib/util/bufferutils'; @@ -25,6 +26,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = null; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/react-native/index.ts b/src/platform/react-native/index.ts index e0539aa92a..e255955093 100644 --- a/src/platform/react-native/index.ts +++ b/src/platform/react-native/index.ts @@ -3,6 +3,7 @@ import { DefaultRest } from '../../common/lib/client/defaultrest'; import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -29,6 +30,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: decodeVcdiff }; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/web/index.ts b/src/platform/web/index.ts index 27d6c9556b..4933d19ff2 100644 --- a/src/platform/web/index.ts +++ b/src/platform/web/index.ts @@ -27,6 +27,11 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = Transports; Platform.WebStorage = WebStorage; +// To use vcdiff on web you must use the modular variant of the library +Platform.Vcdiff = { + supported: false, + errorMessage: 'For vcdiff functionality, you must use the modular variant of ably-js', +}; for (const clientClass of [DefaultRest, DefaultRealtime]) { clientClass.Crypto = Crypto; diff --git a/src/platform/web/modules.ts b/src/platform/web/modules.ts index 683dace27d..e730858121 100644 --- a/src/platform/web/modules.ts +++ b/src/platform/web/modules.ts @@ -22,6 +22,7 @@ Platform.Http = Http; Platform.Config = Config; Platform.Transports = ModulesTransports; Platform.WebStorage = WebStorage; +Platform.Vcdiff = { supported: true, bundledDecode: null }; Http.bundledRequestImplementations = modulesBundledRequestImplementations; @@ -49,6 +50,7 @@ export * from './modules/msgpack'; export * from './modules/realtimepresence'; export * from './modules/transports'; export * from './modules/http'; +export * from './modules/vcdiff'; export { Rest } from '../../common/lib/client/rest'; export { FilteredSubscriptions as MessageInteractions } from '../../common/lib/client/filteredsubscriptions'; export { RealtimePublishing } from '../../common/lib/client/realtimepublishing'; diff --git a/src/platform/web/modules/vcdiff.ts b/src/platform/web/modules/vcdiff.ts new file mode 100644 index 0000000000..002dad7ea3 --- /dev/null +++ b/src/platform/web/modules/vcdiff.ts @@ -0,0 +1 @@ +export { decode as Vcdiff } from '@ably/vcdiff-decoder'; diff --git a/test/browser/modules.test.js b/test/browser/modules.test.js index 1641f1e6a5..c32801e425 100644 --- a/test/browser/modules.test.js +++ b/test/browser/modules.test.js @@ -21,9 +21,10 @@ import { XHRRequest, MessageInteractions, RealtimePublishing, + Vcdiff, } from '../../build/modules/index.js'; -function registerAblyModulesTests(helper) { +function registerAblyModulesTests(helper, registerDeltaTests) { describe('browser/modules', function () { this.timeout(10 * 1000); const expect = chai.expect; @@ -751,14 +752,45 @@ function registerAblyModulesTests(helper) { }); }); }); + + // Tests for the Vcdiff module + // + // Note: Unlike the other tests in this file, which only test how the + // absence or presence of a module affects the client, assuming that the + // underlying functionality is tested in detail in the test suite for the + // default variant of the library, the tests for the Vcdiff module actually + // test the library’s delta encoding functionality. This is because on web, + // delta encoding functionality is only available in the modular variant of + // the library. + (() => { + const config = { + createRealtimeWithDeltaPlugin: (options) => { + return new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + RealtimePublishing, + Vcdiff, + }); + }, + createRealtimeWithoutDeltaPlugin: (options) => { + return new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + RealtimePublishing, + }); + }, + }; + + registerDeltaTests('Vcdiff', config); + })(); }); } // This function is called by browser_setup.js once `require` is available window.registerAblyModulesTests = async () => { return new Promise((resolve) => { - require(['shared_helper'], (helper) => { - registerAblyModulesTests(helper); + require(['shared_helper', 'delta_tests'], (helper, registerDeltaTests) => { + registerAblyModulesTests(helper, registerDeltaTests); resolve(); }); }); diff --git a/test/common/globals/named_dependencies.js b/test/common/globals/named_dependencies.js index 105f075baa..60dd9e6088 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -4,10 +4,6 @@ define(function () { // Ably modules ably: { browser: 'build/ably', node: 'build/ably-node' }, 'ably.noencryption': { browser: 'build/ably.noencryption' }, - 'vcdiff-decoder': { - browser: 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder', - node: 'node_modules/@ably/vcdiff-decoder', - }, // test modules globals: { browser: 'test/common/globals/environment', node: 'test/common/globals/environment' }, diff --git a/test/realtime/delta.test.js b/test/realtime/delta.test.js index 83575f46f5..14a63beaa8 100644 --- a/test/realtime/delta.test.js +++ b/test/realtime/delta.test.js @@ -1,5 +1,29 @@ 'use strict'; -define(['delta_tests'], function (registerDeltaTests) { - registerDeltaTests('realtime/delta'); +define(['shared_helper', 'delta_tests'], function (helper, runDeltaTests) { + const Platform = helper.Ably.Realtime.Platform; + + let config; + + if (Platform.Vcdiff.supported) { + if (Platform.Vcdiff.bundledDecode) { + config = { + createRealtimeWithDeltaPlugin: (options) => { + return helper.AblyRealtime(options); + }, + }; + } else { + throw new Error( + 'vcdiff is supported but not bundled; this should only be the case for the modular variant of the library, which this test doesn’t exercise' + ); + } + } else { + config = { + createRealtimeWithoutDeltaPlugin: (options) => { + return new helper.AblyRealtime(options); + }, + }; + } + + runDeltaTests('realtime/delta', config); }); diff --git a/test/realtime/shared/delta_tests.js b/test/realtime/shared/delta_tests.js index 20ccf8183e..0de7ebde1f 100644 --- a/test/realtime/shared/delta_tests.js +++ b/test/realtime/shared/delta_tests.js @@ -1,5 +1,5 @@ -define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, vcdiffDecoder, async, chai) { - function registerDeltaTests(describeLabel) { +define(['shared_helper', 'async', 'chai'], function (helper, async, chai) { + function registerDeltaTests(describeLabel, config) { var expect = chai.expect; var displayError = helper.displayError; var closeAndFinish = helper.closeAndFinish; @@ -17,13 +17,26 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v return JSON.stringify(a) === JSON.stringify(b); } - function getTestVcdiffDecoder() { + function getTestVcdiffDecoder(realtime) { + if (!realtime._decodeVcdiff) { + throw new Error('Expected client to expose vcdiff decoder via _decodeVcdiff property'); + } + + let numberOfCalls = 0; + + const originalDecodeVcdiff = realtime._decodeVcdiff; + const testDecodeVcdiff = function (delta, base) { + numberOfCalls++; + return originalDecodeVcdiff(delta, base); + }; + + realtime._decodeVcdiff = testDecodeVcdiff; + return { - numberOfCalls: 0, - decode: function (delta, base) { - this.numberOfCalls++; - return vcdiffDecoder.decode(delta, base); + get numberOfCalls() { + return numberOfCalls; }, + decode: testDecodeVcdiff, }; } @@ -39,244 +52,237 @@ define(['shared_helper', 'vcdiff-decoder', 'async', 'chai'], function (helper, v }); }); - it('deltaPlugin', function (done) { - var testName = 'deltaPlugin'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - - channel.on('attaching', function (stateChange) { - done( - new Error( - 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason) - ) - ); - }); - - channel.subscribe(function (message) { - try { - var index = Number(message.name); - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + if (config.createRealtimeWithDeltaPlugin) { + it('deltaPlugin', function (done) { + var testName = 'deltaPlugin'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - if (index === testData.length - 1) { - expect(testVcdiffDecoder.numberOfCalls).to.equal( - testData.length - 1, - 'Check number of delta messages' - ); - closeAndFinish(done, realtime); - } - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } - }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + channel.on('attaching', function (stateChange) { + done( + new Error( + 'Channel reattaching, presumably due to decode failure; reason =' + displayError(stateChange.reason) + ) + ); + }); + + channel.subscribe(function (message) { + try { + var index = Number(message.name); + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + + if (index === testData.length - 1) { + expect(testVcdiffDecoder.numberOfCalls).to.equal( + testData.length - 1, + 'Check number of delta messages' + ); + closeAndFinish(done, realtime); + } + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - it('unusedPlugin', function (done) { - var testName = 'unusedPlugin'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.subscribe(function (message) { - try { - var index = Number(message.name); - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + if (config.createRealtimeWithDeltaPlugin) { + it('unusedPlugin', function (done) { + var testName = 'unusedPlugin'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName); - if (index === testData.length - 1) { - expect(testVcdiffDecoder.numberOfCalls).to.equal(0, 'Check number of delta messages'); - closeAndFinish(done, realtime); - } - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } - }); + channel.subscribe(function (message) { + try { + var index = Number(message.name); + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + if (index === testData.length - 1) { + expect(testVcdiffDecoder.numberOfCalls).to.equal(0, 'Check number of delta messages'); + closeAndFinish(done, realtime); + } + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } + + if (config.createRealtimeWithDeltaPlugin) { + it('lastMessageNotFoundRecovery', function (done) { + var testName = 'lastMessageNotFoundRecovery'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + var testVcdiffDecoder = getTestVcdiffDecoder(realtime); + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - it('lastMessageNotFoundRecovery', function (done) { - var testName = 'lastMessageNotFoundRecovery'; - try { - var testVcdiffDecoder = getTestVcdiffDecoder(); - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: testVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.subscribe(function (message) { - var index = Number(message.name); - try { - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - } catch (err) { + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } + channel.subscribe(function (message) { + var index = Number(message.name); + try { + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + } catch (err) { + closeAndFinish(done, realtime, err); + } - if (index === 1) { - /* Simulate issue */ - channel._lastPayload.messageId = null; - channel.once('attaching', function (stateChange) { + if (index === 1) { + /* Simulate issue */ + channel._lastPayload.messageId = null; + channel.once('attaching', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + channel.on('attaching', function (stateChange) { + closeAndFinish( + done, + realtime, + new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)) + ); + }); + }); + } else if (index === testData.length - 1) { try { - expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + expect(testVcdiffDecoder.numberOfCalls).to.equal( + testData.length - 2, + 'Check number of delta messages' + ); } catch (err) { closeAndFinish(done, realtime, err); return; } - channel.on('attaching', function (stateChange) { - closeAndFinish( - done, - realtime, - new Error('Check no further decode failures; reason =' + displayError(stateChange.reason)) - ); - }); - }); - } else if (index === testData.length - 1) { - try { - expect(testVcdiffDecoder.numberOfCalls).to.equal( - testData.length - 2, - 'Check number of delta messages' - ); - } catch (err) { - closeAndFinish(done, realtime, err); - return; + closeAndFinish(done, realtime); } - closeAndFinish(done, realtime); - } - }); + }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - it('deltaDecodeFailureRecovery', function (done) { - var testName = 'deltaDecodeFailureRecovery'; - try { - var failingTestVcdiffDecoder = { - decode: function (delta, base) { + if (config.createRealtimeWithDeltaPlugin) { + it('deltaDecodeFailureRecovery', function (done) { + var testName = 'deltaDecodeFailureRecovery'; + try { + var realtime = config.createRealtimeWithDeltaPlugin(helper.ablyClientOptions()); + + realtime._decodeVcdiff = function (delta, base) { throw new Error('Failed to decode delta.'); - }, - }; - - var realtime = helper.AblyRealtime({ - plugins: { - vcdiff: failingTestVcdiffDecoder, - }, - }); - var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.on('attaching', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); - channel.subscribe(function (message) { - var index = Number(message.name); - try { - expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; - } catch (err) { + }; + + var channel = realtime.channels.get(testName, { params: { delta: 'vcdiff' } }); + + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); } + channel.on('attaching', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40018, 'Check error code passed through per RTL18c'); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + channel.subscribe(function (message) { + var index = Number(message.name); + try { + expect(equals(testData[index], message.data), 'Check message.data').to.be.ok; + } catch (err) { + closeAndFinish(done, realtime, err); + } - if (index === testData.length - 1) { - closeAndFinish(done, realtime); - } - }); + if (index === testData.length - 1) { + closeAndFinish(done, realtime); + } + }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } - /* Check that channel becomes failed if we get deltas when we don't have a vcdiff plugin */ - it('noPlugin', function (done) { - try { - var realtime = helper.AblyRealtime(); - var channel = realtime.channels.get('noPlugin', { params: { delta: 'vcdiff' } }); - - whenPromiseSettles(channel.attach(), function (err) { - if (err) { - closeAndFinish(done, realtime, err); - } - channel.once('failed', function (stateChange) { - try { - expect(stateChange.reason.code).to.equal(40019, 'Check error code'); - } catch (err) { + if (config.createRealtimeWithoutDeltaPlugin) { + /* Check that channel becomes failed if we get deltas when we don't have a vcdiff plugin */ + it('noPlugin', function (done) { + try { + var realtime = config.createRealtimeWithoutDeltaPlugin(helper.ablyClientOptions()); + var channel = realtime.channels.get('noPlugin', { params: { delta: 'vcdiff' } }); + + whenPromiseSettles(channel.attach(), function (err) { + if (err) { closeAndFinish(done, realtime, err); - return; } - closeAndFinish(done, realtime); - }); - async.timesSeries(testData.length, function (i, cb) { - channel.publish(i.toString(), testData[i], cb); + channel.once('failed', function (stateChange) { + try { + expect(stateChange.reason.code).to.equal(40019, 'Check error code'); + } catch (err) { + closeAndFinish(done, realtime, err); + return; + } + closeAndFinish(done, realtime); + }); + async.timesSeries(testData.length, function (i, cb) { + channel.publish(i.toString(), testData[i], cb); + }); }); - }); - monitorConnection(done, realtime); - } catch (err) { - closeAndFinish(done, realtime, err); - } - }); + monitorConnection(done, realtime); + } catch (err) { + closeAndFinish(done, realtime, err); + } + }); + } }); } diff --git a/test/support/browser_file_list.js b/test/support/browser_file_list.js index 4d0fa8522d..70ce3fa3f9 100644 --- a/test/support/browser_file_list.js +++ b/test/support/browser_file_list.js @@ -10,7 +10,6 @@ window.__testFiles__.files = { 'build/ably.noencryption.min.js': true, 'browser/lib/util/base64.js': true, 'node_modules/async/lib/async.js': true, - 'node_modules/@ably/vcdiff-decoder/dist/vcdiff-decoder.js': true, 'test/common/globals/environment.js': true, 'test/common/globals/named_dependencies.js': true, 'test/common/modules/client_module.js': true, diff --git a/test/support/browser_setup.js b/test/support/browser_setup.js index 62da2cb8f6..2325cc19df 100644 --- a/test/support/browser_setup.js +++ b/test/support/browser_setup.js @@ -61,9 +61,6 @@ require([(baseUrl + '/test/common/globals/named_dependencies.js').replace('//', 'browser-base64': { exports: 'Base64', }, - 'vcdiff-decoder': { - exports: 'vcdiffDecoder', - }, }, // dynamically load all test files