cypress-mailisk 2.0.0 → 2.1.1

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.
package/README.md CHANGED
@@ -49,7 +49,7 @@ This Cypress command does a few extra things out of the box compared to calling
49
49
 
50
50
  - By default it uses the `wait` flag. This means the call won't return until at least one email is received. Disabling this flag via `wait: false` can cause it to return an empty response immediately.
51
51
  - The request timeout is adjustable by passing `timeout` in the request options. By default it uses a timeout of 5 minutes.
52
- - By default it returns emails in the last 15 minutes. This ensures that only new emails are returned. Without this, older emails would also be returned, potentially disrupting you if you were waiting for a specific email. This can be overriden by passing the `from_timestamp` parameter (`from_timestamp: 0` will disable filtering by email age).
52
+ - By default it returns emails in the last 15 minutes. This ensures that only new emails are returned. This can be overriden by passing the `from_timestamp` parameter (`from_timestamp: 0` will disable filtering by email age).
53
53
 
54
54
  ```js
55
55
  // timeout of 5 minute
@@ -60,9 +60,125 @@ cy.mailiskSearchInbox(namespace, {}, { timeout: 1000 * 60 });
60
60
  cy.mailiskSearchInbox(namespace, { wait: false });
61
61
  ```
62
62
 
63
- The implementation of these features is explained in the [NodeJS Guide](/guides/nodejs).
63
+ For the full list of filters and their description see the [Search Inbox](/api-reference/search-inbox#request-1) endpoint reference.
64
64
 
65
- ## Example
65
+ ### cy.mailiskGetAttachment
66
+
67
+ This command retrieves attachment metadata including download URL, filename, content type, and size.
68
+
69
+ ```js
70
+ cy.mailiskGetAttachment('yournamespace', 'attachment-id').then((attachment) => {
71
+ console.log(attachment.data.filename);
72
+ console.log(attachment.data.content_type);
73
+ console.log(attachment.data.size);
74
+ });
75
+ ```
76
+
77
+ ### cy.mailiskDownloadAttachment
78
+
79
+ This command downloads the actual attachment content as a Buffer. It first retrieves the attachment metadata, then downloads the file from the provided download URL.
80
+
81
+ ```js
82
+ cy.mailiskDownloadAttachment('yournamespace', 'attachment-id').then((buffer) => {
83
+ // Save attachment to file
84
+ cy.writeFile('downloads/attachment.pdf', buffer);
85
+ });
86
+ ```
87
+
88
+ Both commands accept optional request options as a third parameter:
89
+
90
+ ```js
91
+ cy.mailiskGetAttachment('yournamespace', 'attachment-id', { timeout: 30000 });
92
+ cy.mailiskDownloadAttachment('yournamespace', 'attachment-id', { timeout: 60000 });
93
+ ```
94
+
95
+ ### Filter by TO address
96
+
97
+ The `to_addr_prefix` option allows filtering by the email's TO address. Specifically the TO address has to start with this.
98
+
99
+ For example, if someone sends an email to `my-user-1@yournamespace.mailisk.net`, you can filter it by using `my-user-1@`:
100
+
101
+ ```js
102
+ cy.mailiskSearchInbox(namespace, {
103
+ to_addr_prefix: 'my-user-1@',
104
+ });
105
+ ```
106
+
107
+ ### Filter by FROM address
108
+
109
+ The `from_addr_includes` option allows filtering by the email's FROM address. Specifically the TO address has to include this. Note that this is different from the to address as it is _includes_ not _prefix_.
110
+
111
+ For example, if someone sends an email from the `example.com` domain we could filter like so:
112
+
113
+ ```js
114
+ cy.mailiskSearchInbox(namespace, {
115
+ from_addr_includes: '@example.com',
116
+ });
117
+ ```
118
+
119
+ If we know a specific email address we want to listen to we can do this:
120
+
121
+ ```js
122
+ cy.mailiskSearchInbox(namespace, {
123
+ from_addr_includes: 'no-reply@example.com',
124
+ });
125
+ ```
126
+
127
+ ### Filter by Subject
128
+
129
+ The `subject_includes` option allows filtering by the email's Subject. Specifically the Subject has to include this (case-insensitive).
130
+
131
+ If we're testing password reset that sends an email with the subject `Password reset request`. We could filter by something like this:
132
+
133
+ ```js
134
+ cy.mailiskSearchInbox(namespace, {
135
+ subject_includes: 'password reset request',
136
+ });
137
+ ```
138
+
139
+ ## Common test cases
140
+
141
+ ### Working with email attachments
142
+
143
+ This example demonstrates how to search for emails with attachments and download them:
144
+
145
+ ```js
146
+ describe('Test email attachments', () => {
147
+ const namespace = 'yournamespace';
148
+ const testEmailAddr = `test.user@${namespace}.mailisk.net`;
149
+
150
+ it('Finds email with attachment and downloads it', () => {
151
+ cy.mailiskSearchInbox(namespace, {
152
+ to_addr_prefix: testEmailAddr,
153
+ subject_includes: 'invoice',
154
+ }).then((response) => {
155
+ expect(response.data).to.not.be.empty;
156
+ const email = response.data[0];
157
+
158
+ // Check if email has attachments
159
+ expect(email.attachments).to.not.be.empty;
160
+ const attachment = email.attachments[0];
161
+
162
+ // Get attachment metadata
163
+ cy.mailiskGetAttachment(attachment.id).then((attachmentData) => {
164
+ expect(attachmentData.data.filename).to.contain('.pdf');
165
+ expect(attachmentData.data.content_type).to.equal('application/pdf');
166
+
167
+ // Download the attachment
168
+ cy.mailiskDownloadAttachment(attachment.id).then((buffer) => {
169
+ // Save to downloads folder
170
+ cy.writeFile(`downloads/${attachmentData.data.filename}`, buffer);
171
+
172
+ // Verify file was downloaded
173
+ cy.readFile(`downloads/${attachmentData.data.filename}`).should('exist');
174
+ });
175
+ });
176
+ });
177
+ });
178
+ });
179
+ ```
180
+
181
+ ### Password reset page
66
182
 
67
183
  This example demonstrates going to a password reset page, requesting a new password, receiving reset code link via email and finally setting the new password.
68
184
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cypress-mailisk",
3
- "version": "2.0.0",
3
+ "version": "2.1.1",
4
4
  "description": "Mailisk library for Cypress",
5
5
  "keywords": [
6
6
  "mailisk",
@@ -13,7 +13,10 @@
13
13
  "main": "index.js",
14
14
  "types": "src/mailiskCommands.d.ts",
15
15
  "scripts": {
16
- "tsc": "node_modules/.bin/tsc src/mailiskCommands.d.ts --types node"
16
+ "tsc": "node_modules/.bin/tsc src/mailiskCommands.d.ts --types node",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch",
19
+ "test:coverage": "jest --coverage"
17
20
  },
18
21
  "peerDependencies": {
19
22
  "cypress": ">= 2.1.0"
@@ -32,6 +35,8 @@
32
35
  },
33
36
  "homepage": "https://github.com/mailisk-app/cypress-mailisk#readme",
34
37
  "devDependencies": {
35
- "typescript": "^4.7.4"
38
+ "typescript": "^4.7.4",
39
+ "jest": "^29.0.0",
40
+ "@jest/globals": "^29.0.0"
36
41
  }
37
42
  }
@@ -7,6 +7,17 @@ export interface EmailAddress {
7
7
  name?: string;
8
8
  }
9
9
 
10
+ export interface EmailAttachment {
11
+ /** Unique identifier for the attachment */
12
+ id: string;
13
+ /** Filename of the attachment */
14
+ filename: string;
15
+ /** Content type of the attachment */
16
+ content_type: string;
17
+ /** Size in bytes of the attachment */
18
+ size: number;
19
+ }
20
+
10
21
  export interface Email {
11
22
  /** Namespace scoped ID */
12
23
  id: string;
@@ -32,6 +43,8 @@ export interface Email {
32
43
  expires_timestamp: number;
33
44
  /** The spam score as reported by SpamAssassin */
34
45
  spam_score?: number;
46
+ /** The attachments of the email */
47
+ attachments?: EmailAttachment[];
35
48
  }
36
49
 
37
50
  export interface SearchInboxParams {
@@ -92,6 +105,17 @@ export interface SearchInboxResponse {
92
105
  data: Email[];
93
106
  }
94
107
 
108
+ export interface GetAttachmentResponse {
109
+ data: {
110
+ id: string;
111
+ filename: string;
112
+ content_type: string;
113
+ size: number;
114
+ expires_at: string | null;
115
+ download_url: string;
116
+ };
117
+ }
118
+
95
119
  declare global {
96
120
  namespace Cypress {
97
121
  interface Chainable {
@@ -111,6 +135,32 @@ declare global {
111
135
  */
112
136
  options?: Partial<Cypress.RequestOptions>,
113
137
  ): Cypress.Chainable<SearchInboxResponse>;
138
+
139
+ mailiskGetAttachment(
140
+ /**
141
+ * The attachment ID to retrieve.
142
+ */
143
+ attachmentId: string,
144
+ /**
145
+ * Request options.
146
+ *
147
+ * See https://docs.cypress.io/api/commands/request#Arguments
148
+ */
149
+ options?: Partial<Cypress.RequestOptions>,
150
+ ): Cypress.Chainable<GetAttachmentResponse>;
151
+
152
+ mailiskDownloadAttachment(
153
+ /**
154
+ * The attachment ID to download.
155
+ */
156
+ attachmentId: string,
157
+ /**
158
+ * Request options.
159
+ *
160
+ * See https://docs.cypress.io/api/commands/request#Arguments
161
+ */
162
+ options?: Partial<Cypress.RequestOptions>,
163
+ ): Cypress.Chainable<Buffer>;
114
164
  }
115
165
  }
116
166
  }
@@ -2,7 +2,7 @@ const Request = require('./request');
2
2
 
3
3
  class MailiskCommands {
4
4
  static get cypressCommands() {
5
- return ['mailiskSetApiKey', 'mailiskListNamespaces', 'mailiskSearchInbox'];
5
+ return ['mailiskSetApiKey', 'mailiskListNamespaces', 'mailiskSearchInbox', 'mailiskGetAttachment', 'mailiskDownloadAttachment'];
6
6
  }
7
7
 
8
8
  constructor() {
@@ -66,6 +66,16 @@ class MailiskCommands {
66
66
  return this.request.get(`api/emails/${namespace}/inbox?${urlParams.toString()}`, _options);
67
67
  }
68
68
  }
69
+
70
+ mailiskGetAttachment(attachmentId, options = {}) {
71
+ return this.request.get(`api/attachments/${attachmentId}`, options);
72
+ }
73
+
74
+ mailiskDownloadAttachment(attachmentId, options = {}) {
75
+ return this.mailiskGetAttachment(attachmentId, options).then((attachment) => {
76
+ return this.request.getBinary(attachment.data.download_url, options);
77
+ });
78
+ }
69
79
  }
70
80
 
71
81
  module.exports = MailiskCommands;
package/src/request.js CHANGED
@@ -81,6 +81,24 @@ class Request {
81
81
  del(path, opts) {
82
82
  return this.request('DELETE', path, undefined, opts);
83
83
  }
84
+
85
+ getBinary(url, opts) {
86
+ // For binary downloads, we need to use the full URL directly
87
+ const options = {
88
+ method: 'GET',
89
+ url: url,
90
+ encoding: null, // This tells Cypress to return binary data
91
+ ...opts,
92
+ };
93
+ options.failOnStatusCode = false;
94
+ return cy.request(options).then((response) => {
95
+ if (response.isOkStatusCode) {
96
+ return Buffer.from(response.body);
97
+ }
98
+ // Use the same error handling as regular requests
99
+ return this.getResponseHandler()(response);
100
+ });
101
+ }
84
102
  }
85
103
 
86
104
  module.exports = Request;