Skip to content

Commit bb35686

Browse files
committed
use native fetch
1 parent d18924b commit bb35686

18 files changed

Lines changed: 280 additions & 117 deletions

File tree

Bunoshfile.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import fs from 'node:fs'
22
import path from 'node:path'
33
import { execSync } from 'node:child_process'
4-
import axios from 'axios'
54
import semver from 'semver'
65

76
const { shell, writeToFile, copyFile, task } = global.bunosh
@@ -156,11 +155,19 @@ export async function docsExternalHelpers() {
156155
*/
157156
export async function docsExternalPlugins() {
158157
say('Building Vue plugin docs')
159-
const resp = await axios.get('https://raw.githubusercontent.com/codecept-js/vue-cli-plugin-codeceptjs-puppeteer/master/README.md')
158+
const resp = await fetch('https://raw.githubusercontent.com/codecept-js/vue-cli-plugin-codeceptjs-puppeteer/master/README.md')
159+
const body = await resp.text()
160160

161161
await writeToFile('docs/vue.md', line => {
162-
line`---\npermalink: /vue\nlayout: Section\nsidebar: false\ntitle: Testing Vue Apps\n---\n\n`
163-
line`${resp.data}`
162+
line`---
163+
permalink: /vue
164+
layout: Section
165+
sidebar: false
166+
title: Testing Vue Apps
167+
---
168+
169+
`
170+
line`${body}`
164171
})
165172
}
166173

@@ -480,12 +487,18 @@ export async function contributorFaces() {
480487
const token = process.env.GH_TOKEN
481488

482489
try {
483-
const response = await axios.get(`https://api.github.com/repos/${owner}/${repo}/contributors`, {
490+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/contributors`, {
484491
headers: { Authorization: `token ${token}` },
485492
})
486493

494+
if (!response.ok) {
495+
throw new Error(`GitHub API error: ${response.statusText}`)
496+
}
497+
498+
const data = await response.json()
499+
487500
const excludeUsers = ['dependabot[bot]', 'actions-user']
488-
const filteredContributors = response.data.filter(contributor => !excludeUsers.includes(contributor.login))
501+
const filteredContributors = data.filter(contributor => !excludeUsers.includes(contributor.login))
489502

490503
const contributors = filteredContributors.map(contributor => {
491504
return `

docs/helpers/GraphQL.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ title: GraphQL
1212
**Extends Helper**
1313

1414
GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests.
15-
[Axios][1] library is used to perform requests.
15+
native fetch API is used to perform requests.
1616

1717
## Configuration
1818

@@ -51,15 +51,15 @@ this.helpers['GraphQL']._executeQuery({
5151

5252
### _executeQuery
5353

54-
Executes query via axios call
54+
Executes query via fetch call
5555

5656
#### Parameters
5757

5858
* `request` **[object][2]** 
5959

6060
### _prepareGraphQLRequest
6161

62-
Prepares request for axios call
62+
Prepares request for fetch call
6363

6464
#### Parameters
6565

@@ -145,7 +145,7 @@ const user = response.data.data;
145145

146146
Returns **any** Promise<any>
147147

148-
[1]: https://github.com/axios/axios
148+
149149

150150
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
151151

docs/helpers/REST.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ title: REST
1212
**Extends Helper**
1313

1414
REST helper allows to send additional requests to the REST API during acceptance tests.
15-
[Axios][1] library is used to perform requests.
15+
native fetch API is used to perform requests.
1616

1717

1818

@@ -104,7 +104,7 @@ this.helpers['REST']._executeRequest({
104104

105105
### _executeRequest
106106

107-
Executes axios request
107+
Executes fetch request
108108

109109
#### Parameters
110110

@@ -274,7 +274,7 @@ I.setRequestTimeout(10000); // In milliseconds
274274

275275
* `newTimeout` **[number][5]** timeout in milliseconds
276276

277-
[1]: https://github.com/axios/axios
277+
278278

279279
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
280280

docs/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ You'll also need the Android SDK and an emulator or device, or Xcode and a simul
8585

8686
## API testing
8787

88-
REST and GraphQL tests need no browser — the helpers are built in (they use `axios`, already a dependency):
88+
REST and GraphQL tests need no browser — the helpers are built in (they use native `fetch`):
8989

9090
```sh
9191
npm i codeceptjs --save-dev

example-esm/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
},
1111
"dependencies": {
1212
"codeceptjs": "file:..",
13-
"axios": "^1.6.0",
1413
"joi": "^17.11.0"
1514
},
1615
"devDependencies": {},

lib/helper/ApiDataFactory.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ import store from '../store.js'
127127
* * `create`: override create options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/create" }`
128128
* * `delete`: override delete options. Expected format: `{ method: uri }`. Example: `{ "post": "/users/delete/{id}" }`
129129
*
130-
* Requests can also be overridden with a function which returns [axois request config](https://github.com/axios/axios#request-config).
130+
* Requests can also be overridden with a function which returns request config.
131131
*
132132
* ```js
133133
* create: (data) => ({ method: 'post', url: '/posts', data }),
@@ -221,13 +221,7 @@ class ApiDataFactory extends Helper {
221221
}
222222

223223
static _checkRequirements() {
224-
try {
225-
// In ESM, dependencies are already imported at the top
226-
// The import will fail at module load time if dependencies are missing
227-
return null
228-
} catch (e) {
229-
return ['axios']
230-
}
224+
return null
231225
}
232226

233227
_after() {

lib/helper/Appium.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as webdriverio from 'webdriverio'
22
import fs from 'fs'
3-
import axios from 'axios'
43
import { v4 as uuidv4 } from 'uuid'
54

65
import Webdriver from './WebDriver.js'
@@ -180,7 +179,6 @@ class Appium extends Webdriver {
180179

181180
this.isRunning = false
182181
this.appiumV2 = config.appiumV2 !== false
183-
this.axios = axios.create()
184182

185183
if (this.appiumV2 === false) {
186184
console.log('Appium 1.x is no longer maintained by the Appium team (since 2022-01-01).')
@@ -189,6 +187,28 @@ class Appium extends Webdriver {
189187
}
190188
}
191189

190+
async _appiumRequest(options) {
191+
const { method, url, data } = options
192+
const fetchOptions = {
193+
method: method.toUpperCase(),
194+
headers: {
195+
'Content-Type': 'application/json',
196+
},
197+
}
198+
199+
if (data) {
200+
fetchOptions.body = JSON.stringify(data)
201+
}
202+
203+
const response = await fetch(url, fetchOptions)
204+
const body = await response.json()
205+
206+
return {
207+
status: response.status,
208+
data: body,
209+
}
210+
}
211+
192212
_validateConfig(config) {
193213
if (!(config.app || config.platform) && !config.browser) {
194214
throw new Error(`
@@ -646,7 +666,7 @@ class Appium extends Webdriver {
646666
async removeApp(appId, bundleId) {
647667
onlyForApps.call(this, supportedPlatform.android)
648668

649-
return this.axios({
669+
return this._appiumRequest({
650670
method: 'post',
651671
url: `${this._buildAppiumEndpoint()}/appium/device/remove_app`,
652672
data: { appId, bundleId },
@@ -664,7 +684,7 @@ class Appium extends Webdriver {
664684
async resetApp() {
665685
onlyForApps.call(this)
666686
this.isWeb = false // Reset to native context after app reset
667-
return this.axios({
687+
return this._appiumRequest({
668688
method: 'post',
669689
url: `${this._buildAppiumEndpoint()}/appium/app/reset`,
670690
})
@@ -738,7 +758,7 @@ class Appium extends Webdriver {
738758
async seeOrientationIs(orientation) {
739759
onlyForApps.call(this)
740760

741-
const res = await this.axios({
761+
const res = await this._appiumRequest({
742762
method: 'get',
743763
url: `${this._buildAppiumEndpoint()}/orientation`,
744764
})
@@ -762,7 +782,7 @@ class Appium extends Webdriver {
762782
async setOrientation(orientation) {
763783
onlyForApps.call(this)
764784

765-
return this.axios({
785+
return this._appiumRequest({
766786
method: 'post',
767787
url: `${this._buildAppiumEndpoint()}/orientation`,
768788
data: { orientation },
@@ -1011,7 +1031,7 @@ class Appium extends Webdriver {
10111031
async hideDeviceKeyboard() {
10121032
onlyForApps.call(this)
10131033

1014-
return this.axios({
1034+
return this._appiumRequest({
10151035
method: 'post',
10161036
url: `${this._buildAppiumEndpoint()}/appium/device/hide_keyboard`,
10171037
data: {},
@@ -1093,7 +1113,7 @@ class Appium extends Webdriver {
10931113
async tap(locator) {
10941114
const { elementId } = await this.browser.$(parseLocator.call(this, locator))
10951115

1096-
return this.axios({
1116+
return this._appiumRequest({
10971117
method: 'post',
10981118
url: `${this._buildAppiumEndpoint()}/element/${elementId}/click`,
10991119
data: {},
@@ -1545,7 +1565,7 @@ class Appium extends Webdriver {
15451565

15461566
const { elementId } = await this.browser.$(parseLocator.call(this, locator), parseLocator.call(this, context))
15471567

1548-
return this.axios({
1568+
return this._appiumRequest({
15491569
method: 'post',
15501570
url: `${this._buildAppiumEndpoint()}/element/${elementId}/click`,
15511571
data: {},

lib/helper/GraphQL.js

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import axios from 'axios'
21
import HelperModule from '@codeceptjs/helper'
32

43
/**
54
* GraphQL helper allows to send additional requests to a GraphQl endpoint during acceptance tests.
6-
* [Axios](https://github.com/axios/axios) library is used to perform requests.
5+
* native fetch API is used to perform requests.
76
*
87
* ## Configuration
98
*
@@ -39,7 +38,6 @@ import HelperModule from '@codeceptjs/helper'
3938
class GraphQL extends Helper {
4039
constructor(config) {
4140
super(config)
42-
this.axios = axios.create()
4341
this.headers = {}
4442
this.options = {
4543
timeout: 10000,
@@ -50,15 +48,10 @@ class GraphQL extends Helper {
5048
}
5149
this.options = Object.assign(this.options, config)
5250
this.headers = { ...this.options.defaultHeaders }
53-
this.axios.defaults.headers = this.options.defaultHeaders
5451
}
5552

5653
static _checkRequirements() {
57-
try {
58-
require('axios')
59-
} catch (e) {
60-
return ['axios']
61-
}
54+
return null
6255
}
6356

6457
static _config() {
@@ -72,18 +65,18 @@ class GraphQL extends Helper {
7265
}
7366

7467
/**
75-
* Executes query via axios call
68+
* Executes query
7669
*
7770
* @param {object} request
7871
*/
7972
async _executeQuery(request) {
80-
this.axios.defaults.timeout = request.timeout || this.options.timeout
73+
const timeout = request.timeout || this.options.timeout
8174

8275
if (this.headers && this.headers.auth) {
8376
request.auth = this.headers.auth
8477
}
8578

86-
request.headers = Object.assign(request.headers, {
79+
request.headers = Object.assign(request.headers || {}, {
8780
'Content-Type': 'application/json',
8881
})
8982

@@ -96,12 +89,65 @@ class GraphQL extends Helper {
9689
this.debugSection('Request', JSON.stringify(request))
9790

9891
let response
92+
const controller = new AbortController()
93+
const id = setTimeout(() => controller.abort(), timeout)
94+
95+
const url = request.url ? (request.baseURL ? request.baseURL + request.url : request.url) : request.baseURL
96+
const fetchOptions = {
97+
method: request.method || 'POST',
98+
headers: request.headers || {},
99+
signal: controller.signal,
100+
}
101+
102+
if (request.data) {
103+
fetchOptions.body = typeof request.data === 'object' ? JSON.stringify(request.data) : request.data
104+
}
105+
106+
if (request.auth) {
107+
const { username, password } = request.auth
108+
const auth = Buffer.from(`${username}:${password}`).toString('base64')
109+
fetchOptions.headers.Authorization = `Basic ${auth}`
110+
}
111+
99112
try {
100-
response = await this.axios(request)
113+
const fetchResponse = await fetch(url, fetchOptions)
114+
clearTimeout(id)
115+
116+
const headers = {}
117+
fetchResponse.headers.forEach((value, key) => {
118+
headers[key] = value
119+
})
120+
121+
let data
122+
const contentType = headers['content-type']
123+
if (contentType && contentType.includes('application/json')) {
124+
data = await fetchResponse.json()
125+
} else {
126+
data = await fetchResponse.text()
127+
try {
128+
data = JSON.parse(data)
129+
} catch (e) {
130+
// not a json
131+
}
132+
}
133+
134+
response = {
135+
data,
136+
status: fetchResponse.status,
137+
statusText: fetchResponse.statusText,
138+
headers,
139+
config: request,
140+
}
141+
142+
if (!fetchResponse.ok) {
143+
this.debugSection('Response', `Response error. Status code: ${fetchResponse.status}`)
144+
}
101145
} catch (err) {
102-
if (!err.response) throw err
103-
this.debugSection('Response', `Response error. Status code: ${err.response.status}`)
104-
response = err.response
146+
clearTimeout(id)
147+
if (err.name === 'AbortError') {
148+
throw new Error(`Request timed out after ${timeout}ms`)
149+
}
150+
throw err
105151
}
106152

107153
if (this.options.onResponse) {
@@ -113,7 +159,7 @@ class GraphQL extends Helper {
113159
}
114160

115161
/**
116-
* Prepares request for axios call
162+
* Prepares request for fetch call
117163
*
118164
* @param {object} operation
119165
* @param {object} headers

0 commit comments

Comments
 (0)