diff --git a/README.md b/README.md index 55f1ab07e5..d32388a23d 100644 --- a/README.md +++ b/README.md @@ -200,12 +200,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 @@ -466,13 +527,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 0ea246a164..47006020f2 100644 --- a/modules.d.ts +++ b/modules.d.ts @@ -162,6 +162,20 @@ export declare const FetchRequest: unknown; */ export declare const MessageInteractions: 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. */ @@ -215,6 +229,11 @@ export interface ModulesMap { * See {@link MessageInteractions | documentation for the `MessageInteractions` module}. */ MessageInteractions?: typeof MessageInteractions; + + /** + * See {@link Vcdiff | documentation for the `Vcdiff` module}. + */ + Vcdiff?: typeof Vcdiff; } /** diff --git a/scripts/moduleReport.ts b/scripts/moduleReport.ts index f3deda7b30..56ebce35c2 100644 --- a/scripts/moduleReport.ts +++ b/scripts/moduleReport.ts @@ -17,6 +17,7 @@ const moduleNames = [ 'XHRRequest', 'FetchRequest', 'MessageInteractions', + '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 3787709326..821b86b6e5 100644 --- a/src/common/lib/client/baserealtime.ts +++ b/src/common/lib/client/baserealtime.ts @@ -11,13 +11,15 @@ 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 { 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. */ class BaseRealtime extends BaseClient { readonly _RealtimePresence: RealtimePresenceModule | 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; @@ -28,6 +30,7 @@ class BaseRealtime extends BaseClient { Logger.logAction(Logger.LOG_MINOR, 'Realtime()', ''); this._additionalTransportImplementations = BaseRealtime.transportImplementationsFromModules(modules); this._RealtimePresence = modules.RealtimePresence ?? 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 e52730aa32..7a3fb55b5a 100644 --- a/src/common/lib/client/modulesmap.ts +++ b/src/common/lib/client/modulesmap.ts @@ -10,6 +10,7 @@ import { fromValues as presenceMessageFromValues, fromValuesArray as presenceMessagesFromValuesArray, } from '../types/presencemessage'; +import { VcdiffDecoder } from '../types/message'; export interface PresenceMessageModule { presenceMessageFromValues: typeof presenceMessageFromValues; @@ -31,6 +32,7 @@ export interface ModulesMap { XHRRequest?: typeof XHRRequest; FetchRequest?: typeof fetchRequest; MessageInteractions?: typeof FilteredSubscriptions; + Vcdiff?: VcdiffDecoder; } export const allCommonModules: ModulesMap = { Rest }; diff --git a/src/common/lib/client/realtimechannel.ts b/src/common/lib/client/realtimechannel.ts index e83c32e25f..27d409ea38 100644 --- a/src/common/lib/client/realtimechannel.ts +++ b/src/common/lib/client/realtimechannel.ts @@ -122,7 +122,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 5a57dbe073..a88a8fba64 100644 --- a/src/platform/nativescript/index.ts +++ b/src/platform/nativescript/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -30,6 +31,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 d53b83dadd..34ffd94957 100644 --- a/src/platform/nodejs/index.ts +++ b/src/platform/nodejs/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from './lib/util/bufferutils'; @@ -26,6 +27,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 998c2dd19a..1724fa1efa 100644 --- a/src/platform/react-native/index.ts +++ b/src/platform/react-native/index.ts @@ -4,6 +4,7 @@ import { DefaultRealtime } from '../../common/lib/client/defaultrealtime'; import Platform from '../../common/platform'; import ErrorInfo from '../../common/lib/types/errorinfo'; import { fromDeserializedIncludingDependencies as protocolMessageFromDeserialized } from '../../common/lib/types/protocolmessage'; +import { decode as decodeVcdiff } from '@ably/vcdiff-decoder'; // Platform Specific import BufferUtils from '../web/lib/util/bufferutils'; @@ -30,6 +31,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 9f3b0b0a06..a598774244 100644 --- a/src/platform/web/index.ts +++ b/src/platform/web/index.ts @@ -28,6 +28,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 in the browser, 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 0465405a90..16c8343efb 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 { BaseRest, BaseRealtime, ErrorInfo }; 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 775bc05e4e..c8bc4c004c 100644 --- a/test/browser/modules.test.js +++ b/test/browser/modules.test.js @@ -20,9 +20,10 @@ import { FetchRequest, XHRRequest, MessageInteractions, + 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; @@ -705,14 +706,43 @@ 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, + Vcdiff, + }); + }, + createRealtimeWithoutDeltaPlugin: (options) => { + return new BaseRealtime(options, { + WebSocketTransport, + FetchRequest, + }); + }, + }; + + 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 a7234844ec..4d25af26be 100644 --- a/test/common/globals/named_dependencies.js +++ b/test/common/globals/named_dependencies.js @@ -3,10 +3,6 @@ define(function () { return (module.exports = { // Ably modules ably: { browser: 'build/ably', node: 'build/ably-node' }, - '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 5a4ceedc16..7350578e0a 100644 --- a/test/support/browser_file_list.js +++ b/test/support/browser_file_list.js @@ -8,7 +8,6 @@ window.__testFiles__.files = { 'build/ably.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 b668c84a45..0ce70489f1 100644 --- a/test/support/browser_setup.js +++ b/test/support/browser_setup.js @@ -58,9 +58,6 @@ require([(baseUrl + '/test/common/globals/named_dependencies.js').replace('//', 'browser-base64': { exports: 'Base64', }, - 'vcdiff-decoder': { - exports: 'vcdiffDecoder', - }, }, // dynamically load all test files