@learncard/network-plugin 3.2.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,6 +24,9 @@ const mockClient = () => ({
24
24
  },
25
25
  campaigns: {
26
26
  isAdmin: query(),
27
+ dispatch: mutation(),
28
+ previewDispatch: query(),
29
+ sendTestToMe: mutation(),
27
30
  create: mutation(),
28
31
  update: mutation(),
29
32
  get: query(),
@@ -34,7 +37,7 @@ const mockClient = () => ({
34
37
  addAudience: mutation(),
35
38
  pageAudience: query(),
36
39
  getStats: query(),
37
- getMyCampaigns: query(),
40
+ getMyCampaigns: { query: vi.fn<() => Promise<unknown>>().mockResolvedValue([]) },
38
41
  recordEvent: mutation(),
39
42
  enroll: mutation(),
40
43
  },
@@ -43,6 +46,70 @@ const mockClient = () => ({
43
46
  beforeEach(() => vi.clearAllMocks());
44
47
 
45
48
  describe('campaign network plugin methods', () => {
49
+ it('previews recipients and forwards the reviewed token to dispatch', async () => {
50
+ const client = mockClient();
51
+ vi.mocked(getClient).mockResolvedValue(client as never);
52
+ const { methods } = await getLearnCardNetworkPlugin(wallet, 'https://network.example/trpc');
53
+ await methods.campaignsPreviewDispatch(wallet, 'pilot-clr');
54
+ expect(client.campaigns.previewDispatch.query).toHaveBeenCalledWith({
55
+ campaignId: 'pilot-clr',
56
+ });
57
+ await methods.campaignsDispatch(wallet, 'pilot-clr', {
58
+ expectedPreviewToken: 'a'.repeat(64),
59
+ });
60
+ expect(client.campaigns.dispatch.mutate).toHaveBeenCalledWith({
61
+ campaignId: 'pilot-clr',
62
+ expectedPreviewToken: 'a'.repeat(64),
63
+ });
64
+ });
65
+
66
+ it('acknowledges a private test through the existing recordEvent endpoint', async () => {
67
+ const client = mockClient();
68
+ vi.mocked(getClient).mockResolvedValue(client as never);
69
+ const { methods } = await getLearnCardNetworkPlugin(wallet, 'https://network.example/trpc');
70
+ const testId = 'aaaaaaaa-aaaa-4aaa-aaaa-aaaaaaaaaaaa';
71
+ await methods.recordCampaignEvent(wallet, 'pilot-clr', 'shown', testId);
72
+ expect(client.campaigns.recordEvent.mutate).toHaveBeenCalledWith({
73
+ campaignId: 'pilot-clr',
74
+ event: 'shown',
75
+ testId,
76
+ });
77
+ });
78
+ it('validates campaign messages before returning them to the renderer', async () => {
79
+ const client = mockClient();
80
+ vi.mocked(getClient).mockResolvedValue(client as never);
81
+ const { methods } = await getLearnCardNetworkPlugin(wallet, 'https://network.example/trpc');
82
+ const item = {
83
+ campaign: {
84
+ id: 'pilot-clr',
85
+ message: { id: 'pilot-clr', title: 'Welcome', presentation: 'modal' },
86
+ selfEnroll: false,
87
+ },
88
+ state: { shownCount: 0 },
89
+ };
90
+ client.campaigns.getMyCampaigns.query.mockResolvedValue([item]);
91
+ await expect(methods.getMyCampaigns(wallet)).resolves.toMatchObject([item]);
92
+ for (const invalid of [null, {}, 'invalid']) {
93
+ client.campaigns.getMyCampaigns.query.mockResolvedValue(invalid);
94
+ await expect(methods.getMyCampaigns(wallet)).rejects.toThrow('expected an array');
95
+ }
96
+
97
+ for (const message of [
98
+ null,
99
+ { ...item.campaign.message, presentation: 'unsupported' },
100
+ {
101
+ ...item.campaign.message,
102
+ actions: [{ type: 'navigate', path: 'javascript:alert(1)' }],
103
+ },
104
+ ]) {
105
+ client.campaigns.getMyCampaigns.query.mockResolvedValue([
106
+ { ...item, campaign: { ...item.campaign, message } },
107
+ item,
108
+ ]);
109
+ await expect(methods.getMyCampaigns(wallet)).resolves.toMatchObject([item]);
110
+ }
111
+ });
112
+
46
113
  it('forwards every campaign operation to the correct typed procedure', async () => {
47
114
  const client = mockClient();
48
115
  vi.mocked(getClient).mockResolvedValue(client as never);
@@ -70,6 +137,8 @@ describe('campaign network plugin methods', () => {
70
137
  await methods.campaignsAddAudience(wallet, source);
71
138
  await methods.campaignsPageAudience(wallet, { campaignId: campaign.id, limit: 20 });
72
139
  await methods.campaignsGetStats(wallet, campaign.id);
140
+ await methods.campaignsSendTestToMe(wallet, campaign.id);
141
+ await methods.campaignsDispatch(wallet, campaign.id);
73
142
  await methods.getMyCampaigns(wallet);
74
143
  await methods.recordCampaignEvent(wallet, campaign.id, 'shown');
75
144
  await methods.enrollInCampaign(wallet, campaign.id);
@@ -91,6 +160,10 @@ describe('campaign network plugin methods', () => {
91
160
  limit: 20,
92
161
  });
93
162
  expect(client.campaigns.getStats.query).toHaveBeenCalledWith({ campaignId: campaign.id });
163
+ expect(client.campaigns.sendTestToMe.mutate).toHaveBeenCalledWith({
164
+ campaignId: campaign.id,
165
+ });
166
+ expect(client.campaigns.dispatch.mutate).toHaveBeenCalledWith({ campaignId: campaign.id });
94
167
  expect(client.campaigns.getMyCampaigns.query).toHaveBeenCalledWith();
95
168
  expect(client.campaigns.recordEvent.mutate).toHaveBeenCalledWith({
96
169
  campaignId: campaign.id,
@@ -115,3 +188,16 @@ describe('campaign network plugin methods', () => {
115
188
  );
116
189
  });
117
190
  });
191
+
192
+ describe('announcement preferences', () => {
193
+ it('requires a network profile for private preferences', async () => {
194
+ const client = mockClient();
195
+ client.profile.getProfile.query.mockResolvedValue(undefined);
196
+ vi.mocked(getClient).mockResolvedValue(client as never);
197
+ const { methods } = await getLearnCardNetworkPlugin(wallet, 'https://network.example/trpc');
198
+ await expect(methods.getAnnouncementPreferences(wallet)).rejects.toThrow();
199
+ await expect(
200
+ methods.updateAnnouncementPreferences(wallet, { announcementPushOptOut: true })
201
+ ).rejects.toThrow();
202
+ });
203
+ });