Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
scratch-www
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
scratch-www
Commits
ae014228
Unverified
Commit
ae014228
authored
Aug 10, 2020
by
picklesrus
Committed by
GitHub
Aug 10, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4294 from LLK/hotfix/messages
[master] Hotfix/messages
parents
a2651b74
05075712
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
153 additions
and
27 deletions
+153
-27
src/components/navigation/www/navigation.jsx
src/components/navigation/www/navigation.jsx
+32
-26
test/unit/components/navigation.test.jsx
test/unit/components/navigation.test.jsx
+121
-1
No files found.
src/components/navigation/www/navigation.jsx
View file @
ae014228
...
...
@@ -27,56 +27,62 @@ class Navigation extends React.Component {
super
(
props
);
bindAll
(
this
,
[
'
getProfileUrl
'
,
'
handleSearchSubmit
'
'
handleSearchSubmit
'
,
'
pollForMessages
'
]);
this
.
state
=
{
messageCountIntervalId
:
-
1
// javascript method interval id for getting messsage count.
};
// Keep the timeout id so we can cancel it (e.g. when we unmount)
this
.
messageCountTimeoutId
=
-
1
;
}
componentDidMount
()
{
if
(
this
.
props
.
user
)
{
const
intervalId
=
setInterval
(()
=>
{
this
.
props
.
getMessageCount
(
this
.
props
.
user
.
username
);
},
120000
);
// check for new messages every 2 mins.
this
.
setState
({
// eslint-disable-line react/no-did-mount-set-state
messageCountIntervalId
:
intervalId
});
// Setup polling for messages to start in 2 minutes.
const
twoMinInMs
=
2
*
60
*
1000
;
this
.
messageCountTimeoutId
=
setTimeout
(
this
.
pollForMessages
.
bind
(
this
,
twoMinInMs
),
twoMinInMs
);
}
}
componentDidUpdate
(
prevProps
)
{
if
(
prevProps
.
user
!==
this
.
props
.
user
)
{
this
.
props
.
handleCloseAccountNav
();
if
(
this
.
props
.
user
)
{
const
intervalId
=
setInterval
(()
=>
{
this
.
props
.
getMessageCount
(
this
.
props
.
user
.
username
);
},
120000
);
// check for new messages every 2 mins.
this
.
setState
({
// eslint-disable-line react/no-did-update-set-state
messageCountIntervalId
:
intervalId
});
const
twoMinInMs
=
2
*
60
*
1000
;
this
.
messageCountTimeoutId
=
setTimeout
(
this
.
pollForMessages
.
bind
(
this
,
twoMinInMs
),
twoMinInMs
);
}
else
{
// clear message count check, and set to default id.
clearInterval
(
this
.
state
.
messageCountIntervalId
);
// Clear message count check, and set to default id.
if
(
this
.
messageCountTimeoutId
!==
-
1
)
{
clearTimeout
(
this
.
messageCountTimeoutId
);
}
this
.
props
.
setMessageCount
(
0
);
this
.
setState
({
// eslint-disable-line react/no-did-update-set-state
messageCountIntervalId
:
-
1
});
this
.
messageCountTimeoutId
=
-
1
;
}
}
}
componentWillUnmount
()
{
// clear message interval if it exists
if
(
this
.
state
.
messageCountInterval
Id
!==
-
1
)
{
clear
Interval
(
this
.
state
.
messageCountInterval
Id
);
if
(
this
.
messageCountTimeout
Id
!==
-
1
)
{
clear
Timeout
(
this
.
messageCountTimeout
Id
);
this
.
props
.
setMessageCount
(
0
);
this
.
setState
({
messageCountIntervalId
:
-
1
});
this
.
messageCountTimeoutId
=
-
1
;
}
}
getProfileUrl
()
{
if
(
!
this
.
props
.
user
)
return
;
return
`/users/
${
this
.
props
.
user
.
username
}
/`
;
}
pollForMessages
(
ms
)
{
this
.
props
.
getMessageCount
(
this
.
props
.
user
.
username
);
// We only poll if it has been less than 32 minutes.
// Chances of someone actively using the page for that long without
// a navigation is low.
if
(
ms
<
32
*
60
*
1000
)
{
// 32 minutes
const
nextFetch
=
ms
*
2
;
// exponentially back off next fetch time.
const
timeoutId
=
setTimeout
(()
=>
{
this
.
pollForMessages
(
nextFetch
);
},
nextFetch
);
this
.
messageCountTimeoutId
=
timeoutId
;
}
}
handleSearchSubmit
(
formData
)
{
let
targetUrl
=
'
/search/projects
'
;
if
(
formData
.
q
)
{
...
...
test/unit/components/navigation.test.jsx
View file @
ae014228
const
React
=
require
(
'
react
'
);
const
{
shallowWithIntl
}
=
require
(
'
../../helpers/intl-helpers.jsx
'
);
const
{
shallowWithIntl
,
mountWithIntl
}
=
require
(
'
../../helpers/intl-helpers.jsx
'
);
import
configureStore
from
'
redux-mock-store
'
;
const
Navigation
=
require
(
'
../../../src/components/navigation/www/navigation.jsx
'
);
const
Registration
=
require
(
'
../../../src/components/registration/registration.jsx
'
);
...
...
@@ -11,6 +11,7 @@ describe('Navigation', () => {
beforeEach
(()
=>
{
store
=
null
;
jest
.
useFakeTimers
();
});
const
getNavigationWrapper
=
props
=>
{
...
...
@@ -103,4 +104,123 @@ describe('Navigation', () => {
navWrapper
.
find
(
'
a.registrationLink
'
).
simulate
(
'
click
'
,
{
preventDefault
()
{}});
expect
(
navInstance
.
props
.
handleClickRegistration
).
toHaveBeenCalled
();
});
test
(
'
Component sets up message polling when it mounts
'
,
()
=>
{
store
=
mockStore
({
navigation
:
{
registrationOpen
:
false
},
messageCount
:
{
messageCount
:
5
}
});
const
props
=
{
user
:
{
thumbnailUrl
:
'
scratch.mit.edu
'
,
username
:
'
auser
'
},
getMessageCount
:
jest
.
fn
()
};
const
intlWrapper
=
mountWithIntl
(
<
Navigation
{
...
props
}
/>,
{
context
:
{
store
},
childContextTypes
:
{
store
}
});
const
navInstance
=
intlWrapper
.
children
().
find
(
'
Navigation
'
)
.
instance
();
const
twoMin
=
2
*
60
*
1000
;
expect
(
setTimeout
).
toHaveBeenLastCalledWith
(
expect
.
any
(
Function
),
twoMin
);
expect
(
navInstance
.
messageCountTimeoutId
).
not
.
toEqual
(
-
1
);
// Advance timers passed the intial two minutes.
jest
.
advanceTimersByTime
(
twoMin
+
1
);
expect
(
setTimeout
).
toHaveBeenLastCalledWith
(
expect
.
any
(
Function
),
twoMin
*
2
);
expect
(
props
.
getMessageCount
).
toHaveBeenCalled
();
expect
(
navInstance
.
messageCountTimeoutId
).
not
.
toEqual
(
-
1
);
});
test
(
'
Component cancels timers when it unmounts
'
,
()
=>
{
store
=
mockStore
({
navigation
:
{
registrationOpen
:
false
},
messageCount
:
{
messageCount
:
5
}
});
const
props
=
{
user
:
{
thumbnailUrl
:
'
scratch.mit.edu
'
,
username
:
'
auser
'
},
getMessageCount
:
jest
.
fn
()
};
const
intlWrapper
=
mountWithIntl
(
<
Navigation
{
...
props
}
/>,
{
context
:
{
store
},
childContextTypes
:
{
store
}
});
const
navInstance
=
intlWrapper
.
children
().
find
(
'
Navigation
'
)
.
instance
();
const
twoMin
=
2
*
60
*
1000
;
expect
(
setTimeout
).
toHaveBeenLastCalledWith
(
expect
.
any
(
Function
),
twoMin
);
expect
(
navInstance
.
messageCountTimeoutId
).
not
.
toEqual
(
-
1
);
navInstance
.
componentWillUnmount
();
expect
(
clearTimeout
).
toHaveBeenCalledWith
(
expect
.
any
(
Number
));
expect
(
navInstance
.
messageCountTimeoutId
).
toEqual
(
-
1
);
});
test
(
'
pollForMessages polls for messages 5 times
'
,
()
=>
{
store
=
mockStore
({
navigation
:
{
registrationOpen
:
false
},
messageCount
:
{
messageCount
:
5
}
});
const
props
=
{
user
:
{
thumbnailUrl
:
'
scratch.mit.edu
'
,
username
:
'
auser
'
},
getMessageCount
:
jest
.
fn
()
};
const
intlWrapper
=
mountWithIntl
(
<
Navigation
{
...
props
}
/>,
{
context
:
{
store
},
childContextTypes
:
{
store
}
});
const
navInstance
=
intlWrapper
.
children
().
find
(
'
Navigation
'
)
.
instance
();
// Clear the timers and mocks because componentDidMount
// has already called pollForMessages.
jest
.
clearAllTimers
();
jest
.
clearAllMocks
();
let
twoMinInMs
=
2
*
60
*
1000
;
// 2 minutes in ms.
navInstance
.
pollForMessages
(
twoMinInMs
);
expect
(
navInstance
.
messageCountTimeoutId
).
not
.
toEqual
(
-
1
);
// Check that we set the timeout to backoff exponentially
for
(
let
count
=
1
;
count
<
5
;
++
count
)
{
jest
.
advanceTimersByTime
(
twoMinInMs
+
1
);
expect
(
setTimeout
).
toHaveBeenLastCalledWith
(
expect
.
any
(
Function
),
twoMinInMs
*
2
);
expect
(
props
.
getMessageCount
).
toHaveBeenCalledTimes
(
count
);
twoMinInMs
=
twoMinInMs
*
2
;
}
// Exhaust all timers (there shouldn't be any left)
jest
.
runAllTimers
();
// We exponentially back off checking for messages, starting at 2 min
// and stop after 32 minutes so it should happen 5 times total.
expect
(
props
.
getMessageCount
).
toHaveBeenCalledTimes
(
5
);
// setTimeout happens 1 fewer since the original call to
// pollForMessages isn't counted here.
expect
(
setTimeout
).
toHaveBeenCalledTimes
(
4
);
expect
(
navInstance
.
messageCountTimeoutId
).
not
.
toEqual
(
-
1
);
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment