diff --git a/README.md b/README.md
index bd2cf5d..a6afaae 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,8 @@ There are few packages those help the developers to mock the backend requests wh
| `status` | All possible HTTP status codes. | - | `200` |
| `response` | JSON format or function.
Response function is a function that contains request object as a parameter. See the **Custom Response** section for example. | Y | - |
| `delay` | Emulate delayed response time in milliseconds. | - | `0` |
+| `uploadFrameCount` | Emulate response progress, progress will be emitted in delayed time duration. | - | `5` |
+| `uploadTimingFunction` | Emulate response progress, the timing function makes customized progress. Supports `linear`, `ease-in`, `ease-out`. | - | `linear` |
> You can change the **status**, **response** and **delay** from the storybook panel on the fly! :rocket:
diff --git a/src/utils/faker.js b/src/utils/faker.js
index f75fe75..aa59544 100644
--- a/src/utils/faker.js
+++ b/src/utils/faker.js
@@ -121,11 +121,38 @@ export class Faker {
return global.realFetch(input, options);
};
+ /**
+ * mock xhr request
+ * @param {import('mock-xmlhttprequest').MockXhr} xhr
+ */
mockXhrRequest = (xhr) => {
const { method, url, body } = xhr;
const matched = this.matchMock(url, method);
if (matched) {
- const { response, status, delay = 0 } = matched;
+ const {
+ response,
+ status,
+ delay = 0,
+ uploadTimingFunction = 'linear',
+ uploadFrameCount = 5,
+ } = matched;
+ // split delays
+ let timeForTransitionEmit = [];
+ if (delay > 0) {
+ timeForTransitionEmit = getTimingSlice(
+ uploadTimingFunction,
+ uploadFrameCount
+ );
+ }
+
+ timeForTransitionEmit.forEach((progressRatio, timeoutIndex) => {
+ setTimeout(() => {
+ xhr.uploadProgress(
+ progressRatio * xhr.getRequestBodySize()
+ );
+ }, ((timeoutIndex + 1) * +delay) / uploadFrameCount);
+ });
+
setTimeout(() => {
if (typeof response === 'function') {
const data = response(new Request(url, { method, body }));
@@ -172,4 +199,28 @@ export class Faker {
};
}
+function getTimingSlice(timingFunction, sliceCount) {
+ const timeSlice = [];
+
+ if (timingFunction === 'ease-in') {
+ for (let i = 0; i < sliceCount; i++) {
+ timeSlice.splice(i, 0, Math.pow((i + 1) / sliceCount, 1.675));
+ }
+ } else if (timingFunction === 'ease-out') {
+ for (let i = 0; i < sliceCount; i++) {
+ timeSlice.splice(
+ i,
+ 0,
+ 1 - Math.pow(1 - (i + 1) / sliceCount, 1.675)
+ );
+ }
+ } else {
+ // linear
+ for (let i = 0; i < sliceCount; i++) {
+ timeSlice.splice(i, 0, (i + 1) / sliceCount);
+ }
+ }
+ return timeSlice;
+}
+
export default new Faker();
diff --git a/stories/components/non-get-request/index.js b/stories/components/non-get-request/index.js
index 8db4b24..03d0ef9 100644
--- a/stories/components/non-get-request/index.js
+++ b/stories/components/non-get-request/index.js
@@ -16,6 +16,7 @@ export const NonGetRequest = ({ title, method, callApi }) => {
const [todoName, setTodoName] = useState('Item 1');
const [response, setResponse] = useState({});
const [loading, setLoading] = useState(false);
+ const [uploadProgress, setUploadProgress] = useState(0);
const handleSubmit = async (event) => {
event.preventDefault();
@@ -27,6 +28,9 @@ export const NonGetRequest = ({ title, method, callApi }) => {
url,
method,
body: { name: todoName },
+ onUploadProgress: (pev) => {
+ setUploadProgress(pev.loaded / pev.total);
+ },
});
setResponse(apiResponse);
setLoading(false);
@@ -62,7 +66,11 @@ export const NonGetRequest = ({ title, method, callApi }) => {
)}
-