mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-02 19:16:08 +00:00
Add timeout upgrade prompt to the new compile UI (#3528)
* Uncouple account-upgrade and exposted-settings from Angular * Mock socket shim with the correct methods * Extract timeout upgrade prompt to a component GitOrigin-RevId: ee8058b38bf5e20924a21f40d32c5bb0ee06c555
This commit is contained in:
parent
c4b9cc1e25
commit
11e58b5844
17 changed files with 272 additions and 132 deletions
|
@ -369,7 +369,8 @@ module.exports = function(webRouter, privateApiRouter, publicApiRouter) {
|
|||
sentryAllowedOriginRegex: Settings.sentry.allowedOriginRegex,
|
||||
sentryDsn: Settings.sentry.publicDSN,
|
||||
sentryEnvironment: Settings.sentry.environment,
|
||||
sentryRelease: Settings.sentry.release
|
||||
sentryRelease: Settings.sentry.release,
|
||||
enableSubscriptions: Settings.enableSubscriptions
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
|
|
@ -1,55 +1,82 @@
|
|||
[
|
||||
"ask_proj_owner_to_upgrade_for_longer_compiles",
|
||||
"auto_compile",
|
||||
"autocompile_disabled_reason",
|
||||
"autocompile_disabled",
|
||||
"autocomplete",
|
||||
"autocomplete_references",
|
||||
"autocomplete",
|
||||
"blocked_filename",
|
||||
"cancel",
|
||||
"clear_cached_files",
|
||||
"clsi_maintenance",
|
||||
"clsi_unavailable",
|
||||
"collabs_per_proj",
|
||||
"collapse",
|
||||
"common",
|
||||
"compile_error_description",
|
||||
"compile_error_entry_description",
|
||||
"compile_larger_projects",
|
||||
"compile_mode",
|
||||
"compile_terminated_by_user",
|
||||
"compiling",
|
||||
"conflicting_paths_found",
|
||||
"copy_project",
|
||||
"copy",
|
||||
"copying",
|
||||
"create",
|
||||
"creating",
|
||||
"delete",
|
||||
"deleting",
|
||||
"dismiss_error_popup",
|
||||
"done",
|
||||
"download_file",
|
||||
"download_pdf",
|
||||
"duplicate_file",
|
||||
"duplicate_paths_found",
|
||||
"editing",
|
||||
"error",
|
||||
"expand",
|
||||
"fast",
|
||||
"file_outline",
|
||||
"file_already_exists",
|
||||
"file_already_exists_in_this_location",
|
||||
"blocked_filename",
|
||||
"file_already_exists",
|
||||
"file_outline",
|
||||
"file_tree_badge_tooltip",
|
||||
"files_cannot_include_invalid_characters",
|
||||
"find_out_more_about_the_file_outline",
|
||||
"first_error_popup_label",
|
||||
"following_paths_conflict",
|
||||
"free_accounts_have_timeout_upgrade_to_increase",
|
||||
"full_doc_history",
|
||||
"full_screen",
|
||||
"generic_something_went_wrong",
|
||||
"go_to_error_location",
|
||||
"headers",
|
||||
"hide_outline",
|
||||
"hotkeys",
|
||||
"ignore_validation_errors",
|
||||
"invalid_file_name",
|
||||
"latex_error",
|
||||
"learn_how_to_make_documents_compile_quickly",
|
||||
"loading",
|
||||
"log_entry_description",
|
||||
"log_hint_extra_info",
|
||||
"main_file_not_found",
|
||||
"navigation",
|
||||
"math_display",
|
||||
"math_inline",
|
||||
"n_errors_plural",
|
||||
"n_errors",
|
||||
"n_items",
|
||||
"n_warnings_plural",
|
||||
"n_warnings",
|
||||
"navigate_log_source",
|
||||
"navigation",
|
||||
"new_file",
|
||||
"new_folder",
|
||||
"new_name",
|
||||
"no_messages",
|
||||
"normal",
|
||||
"off",
|
||||
"ok",
|
||||
"on",
|
||||
"other_logs_and_files",
|
||||
"other_output_files",
|
||||
|
@ -58,7 +85,9 @@
|
|||
"pdf_compile_try_again",
|
||||
"pdf_rendering_error",
|
||||
"please_compile_pdf_before_download",
|
||||
"please_refresh",
|
||||
"please_set_main_file",
|
||||
"plus_upgraded_accounts_receive",
|
||||
"proj_timed_out_reason",
|
||||
"project_flagged_too_many_compiles",
|
||||
"project_too_large_please_reduce",
|
||||
|
@ -67,6 +96,8 @@
|
|||
"raw_logs",
|
||||
"recompile_from_scratch",
|
||||
"recompile",
|
||||
"refresh",
|
||||
"rename",
|
||||
"review",
|
||||
"run_syntax_check_now",
|
||||
"send_first_message",
|
||||
|
@ -75,13 +106,21 @@
|
|||
"something_went_wrong_rendering_pdf",
|
||||
"somthing_went_wrong_compiling",
|
||||
"split_screen",
|
||||
"start_free_trial",
|
||||
"stop_compile",
|
||||
"stop_on_validation_error",
|
||||
"sure_you_want_to_delete",
|
||||
"sync_to_dropbox",
|
||||
"sync_to_github",
|
||||
"terminated",
|
||||
"timedout",
|
||||
"toggle_compile_options_menu",
|
||||
"toggle_output_files_list",
|
||||
"too_recently_compiled",
|
||||
"total_words",
|
||||
"unlimited_projects",
|
||||
"upgrade_for_longer_compiles",
|
||||
"upload",
|
||||
"validation_issue_description",
|
||||
"validation_issue_entry_description",
|
||||
"view_all_errors",
|
||||
|
@ -89,39 +128,7 @@
|
|||
"view_pdf",
|
||||
"view_warnings",
|
||||
"we_cant_find_any_sections_or_subsections_in_this_file",
|
||||
"your_message",
|
||||
"your_project_has_errors",
|
||||
"copy_project",
|
||||
"copying",
|
||||
"copy",
|
||||
"new_name",
|
||||
"recompile_from_scratch",
|
||||
"run_syntax_check_now",
|
||||
"toggle_compile_options_menu",
|
||||
"sure_you_want_to_delete",
|
||||
"delete",
|
||||
"deleting",
|
||||
"cancel",
|
||||
"new_file",
|
||||
"new_folder",
|
||||
"create",
|
||||
"creating",
|
||||
"upload",
|
||||
"rename",
|
||||
"n_items",
|
||||
"please_refresh",
|
||||
"generic_something_went_wrong",
|
||||
"refresh",
|
||||
"duplicate_file",
|
||||
"error",
|
||||
"invalid_file_name",
|
||||
"ok",
|
||||
"refresh",
|
||||
"word_count",
|
||||
"total_words",
|
||||
"headers",
|
||||
"math_inline",
|
||||
"math_display",
|
||||
"done",
|
||||
"file_tree_badge_tooltip"
|
||||
]
|
||||
"your_message",
|
||||
"your_project_has_errors"
|
||||
]
|
|
@ -2,8 +2,17 @@ import React from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PreviewLogsPaneEntry from './preview-logs-pane-entry'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import { useApplicationContext } from '../../../shared/context/application-context'
|
||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||
import { startFreeTrial } from '../../../main/account-upgrade'
|
||||
|
||||
function PreviewError({ name }) {
|
||||
const { isProjectOwner } = useEditorContext()
|
||||
const {
|
||||
exposedSettings: { enableSubscriptions }
|
||||
} = useApplicationContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
let errorTitle
|
||||
let errorContent
|
||||
|
@ -53,17 +62,101 @@ function PreviewError({ name }) {
|
|||
}
|
||||
|
||||
return errorTitle ? (
|
||||
<PreviewLogsPaneEntry
|
||||
headerTitle={errorTitle}
|
||||
formattedContent={errorContent}
|
||||
entryAriaLabel={t('compile_error_entry_description')}
|
||||
level="error"
|
||||
/>
|
||||
<>
|
||||
<PreviewLogsPaneEntry
|
||||
headerTitle={errorTitle}
|
||||
formattedContent={errorContent}
|
||||
entryAriaLabel={t('compile_error_entry_description')}
|
||||
level="error"
|
||||
/>
|
||||
{name === 'timedout' && enableSubscriptions ? (
|
||||
<TimeoutUpgradePrompt isProjectOwner={isProjectOwner} />
|
||||
) : null}
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
||||
function TimeoutUpgradePrompt({ isProjectOwner }) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
function handleStartFreeTrialClick() {
|
||||
startFreeTrial('compile-timeout')
|
||||
}
|
||||
|
||||
const timeoutUpgradePromptContent = (
|
||||
<>
|
||||
<p>{t('free_accounts_have_timeout_upgrade_to_increase')}</p>
|
||||
<p>{t('plus_upgraded_accounts_receive')}:</p>
|
||||
<div>
|
||||
<ul className="list-unstyled">
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('unlimited_projects')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('collabs_per_proj', { collabcount: 'Multiple' })}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('full_doc_history')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_dropbox')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_github')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('compile_larger_projects')}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{isProjectOwner ? (
|
||||
<p className="text-center">
|
||||
<button
|
||||
className="btn btn-success row-spaced-small"
|
||||
onClick={handleStartFreeTrialClick}
|
||||
>
|
||||
{t('start_free_trial')}
|
||||
</button>
|
||||
</p>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
return (
|
||||
<PreviewLogsPaneEntry
|
||||
headerTitle={
|
||||
isProjectOwner
|
||||
? t('upgrade_for_longer_compiles')
|
||||
: t('ask_proj_owner_to_upgrade_for_longer_compiles')
|
||||
}
|
||||
formattedContent={timeoutUpgradePromptContent}
|
||||
entryAriaLabel={
|
||||
isProjectOwner
|
||||
? t('upgrade_for_longer_compiles')
|
||||
: t('ask_proj_owner_to_upgrade_for_longer_compiles')
|
||||
}
|
||||
level="success"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
PreviewError.propTypes = {
|
||||
name: PropTypes.string.isRequired
|
||||
}
|
||||
|
||||
TimeoutUpgradePrompt.propTypes = {
|
||||
isProjectOwner: PropTypes.bool.isRequired
|
||||
}
|
||||
|
||||
export default PreviewError
|
||||
|
|
|
@ -78,13 +78,15 @@ function PreviewLogEntryHeader({
|
|||
'log-entry-header-error': level === 'error',
|
||||
'log-entry-header-warning': level === 'warning',
|
||||
'log-entry-header-typesetting': level === 'typesetting',
|
||||
'log-entry-header-raw': level === 'raw'
|
||||
'log-entry-header-raw': level === 'raw',
|
||||
'log-entry-header-success': level === 'success'
|
||||
})
|
||||
const logEntryLocationBtnClasses = classNames('log-entry-header-link', {
|
||||
'log-entry-header-link-error': level === 'error',
|
||||
'log-entry-header-link-warning': level === 'warning',
|
||||
'log-entry-header-link-typesetting': level === 'typesetting',
|
||||
'log-entry-header-link-raw': level === 'raw'
|
||||
'log-entry-header-link-raw': level === 'raw',
|
||||
'log-entry-header-link-success': level === 'success'
|
||||
})
|
||||
const headerLogLocationTitle = t('navigate_log_source', {
|
||||
location: file + (line ? `, ${line}` : '')
|
||||
|
@ -255,7 +257,8 @@ PreviewLogsPaneEntry.propTypes = {
|
|||
logType: PropTypes.string,
|
||||
formattedContent: PropTypes.node,
|
||||
extraInfoURL: PropTypes.string,
|
||||
level: PropTypes.oneOf(['error', 'warning', 'typesetting', 'raw']).isRequired,
|
||||
level: PropTypes.oneOf(['error', 'warning', 'typesetting', 'raw', 'success'])
|
||||
.isRequired,
|
||||
customClass: PropTypes.string,
|
||||
showSourceLocationLink: PropTypes.bool,
|
||||
showCloseButton: PropTypes.bool,
|
||||
|
|
|
@ -57,8 +57,8 @@ import './services/validateCaptchaV3'
|
|||
import './services/wait-for'
|
||||
import './filters/formatDate'
|
||||
import './main/event'
|
||||
import './main/account-upgrade'
|
||||
import './main/exposed-settings'
|
||||
import './main/account-upgrade-angular'
|
||||
import './main/exposed-settings-angular'
|
||||
import './main/system-messages'
|
||||
import '../../modules/modules-ide.js'
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import HumanReadableLogs from '../../human-readable-logs/HumanReadableLogs'
|
|||
import BibLogParser from 'libs/bib-log-parser'
|
||||
import PreviewPane from '../../../features/preview/components/preview-pane'
|
||||
import { react2angular } from 'react2angular'
|
||||
import { rootContext } from '../../../shared/context/root-context'
|
||||
import 'ace/ace'
|
||||
const AUTO_COMPILE_MAX_WAIT = 5000
|
||||
// We add a 1 second debounce to sending user changes to server if they aren't
|
||||
|
@ -1152,6 +1153,11 @@ App.controller('ClearCacheModalController', function($scope, $modalInstance) {
|
|||
|
||||
$scope.cancel = () => $modalInstance.dismiss('cancel')
|
||||
})
|
||||
|
||||
// Wrap React component as Angular component. Only needed for "top-level" component
|
||||
App.component('previewPane', react2angular(PreviewPane))
|
||||
App.component(
|
||||
'previewPane',
|
||||
react2angular(
|
||||
rootContext.use(PreviewPane),
|
||||
Object.keys(PreviewPane.propTypes)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ import './main/token-access'
|
|||
import './main/project-list/index'
|
||||
import './main/account-settings'
|
||||
import './main/clear-sessions'
|
||||
import './main/account-upgrade'
|
||||
import './main/account-upgrade-angular'
|
||||
import './main/plans'
|
||||
import './main/post-gateway'
|
||||
import './main/user-membership'
|
||||
|
@ -28,7 +28,7 @@ import './main/register-users'
|
|||
import './main/subscription/team-invite-controller'
|
||||
import './main/subscription/upgrade-subscription'
|
||||
import './main/learn'
|
||||
import './main/exposed-settings'
|
||||
import './main/exposed-settings-angular'
|
||||
import './main/affiliations/components/affiliationForm'
|
||||
import './main/affiliations/components/inputSuggestions'
|
||||
import './main/affiliations/controllers/UserAffiliationsController'
|
||||
|
|
13
services/web/frontend/js/main/account-upgrade-angular.js
Normal file
13
services/web/frontend/js/main/account-upgrade-angular.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import App from '../base'
|
||||
import { startFreeTrial, upgradePlan } from './account-upgrade'
|
||||
|
||||
App.controller('FreeTrialModalController', function($scope, eventTracking) {
|
||||
$scope.buttonClass = 'btn-primary'
|
||||
$scope.startFreeTrial = (source, version) =>
|
||||
startFreeTrial(source, version, $scope, eventTracking)
|
||||
})
|
||||
|
||||
App.controller('UpgradeModalController', function($scope, eventTracking) {
|
||||
$scope.buttonClass = 'btn-primary'
|
||||
$scope.upgradePlan = source => upgradePlan(source, $scope)
|
||||
})
|
|
@ -1,59 +1,49 @@
|
|||
import App from '../base'
|
||||
function startFreeTrial(source, version, $scope, eventTracking) {
|
||||
const plan = 'collaborator_free_trial_7_days'
|
||||
|
||||
export default App.controller('FreeTrialModalController', function(
|
||||
$scope,
|
||||
eventTracking
|
||||
) {
|
||||
$scope.buttonClass = 'btn-primary'
|
||||
|
||||
$scope.startFreeTrial = function(source, version) {
|
||||
const plan = 'collaborator_free_trial_7_days'
|
||||
|
||||
const w = window.open()
|
||||
const go = function() {
|
||||
let url
|
||||
if (typeof ga === 'function') {
|
||||
ga(
|
||||
'send',
|
||||
'event',
|
||||
'subscription-funnel',
|
||||
'upgraded-free-trial',
|
||||
source
|
||||
)
|
||||
}
|
||||
url = `/user/subscription/new?planCode=${plan}&ssp=true`
|
||||
url = `${url}&itm_campaign=${source}`
|
||||
if (version) {
|
||||
url = `${url}&itm_content=${version}`
|
||||
}
|
||||
const w = window.open()
|
||||
const go = function() {
|
||||
let url
|
||||
if (typeof ga === 'function') {
|
||||
ga('send', 'event', 'subscription-funnel', 'upgraded-free-trial', source)
|
||||
}
|
||||
url = `/user/subscription/new?planCode=${plan}&ssp=true`
|
||||
url = `${url}&itm_campaign=${source}`
|
||||
if (version) {
|
||||
url = `${url}&itm_content=${version}`
|
||||
}
|
||||
|
||||
if ($scope) {
|
||||
$scope.startedFreeTrial = true
|
||||
}
|
||||
|
||||
if (eventTracking) {
|
||||
eventTracking.sendMB('subscription-start-trial', { source, plan })
|
||||
|
||||
w.location = url
|
||||
}
|
||||
|
||||
go()
|
||||
w.location = url
|
||||
}
|
||||
})
|
||||
|
||||
App.controller('UpgradeModalController', function($scope, eventTracking) {
|
||||
$scope.buttonClass = 'btn-primary'
|
||||
go()
|
||||
}
|
||||
|
||||
$scope.upgradePlan = function(source) {
|
||||
const w = window.open()
|
||||
const go = function() {
|
||||
let url
|
||||
if (typeof ga === 'function') {
|
||||
ga('send', 'event', 'subscription-funnel', 'upgraded-plan', source)
|
||||
}
|
||||
url = '/user/subscription'
|
||||
function upgradePlan(source, $scope) {
|
||||
const w = window.open()
|
||||
const go = function() {
|
||||
let url
|
||||
if (typeof ga === 'function') {
|
||||
ga('send', 'event', 'subscription-funnel', 'upgraded-plan', source)
|
||||
}
|
||||
url = '/user/subscription'
|
||||
|
||||
if ($scope) {
|
||||
$scope.startedFreeTrial = true
|
||||
|
||||
w.location = url
|
||||
}
|
||||
|
||||
go()
|
||||
w.location = url
|
||||
}
|
||||
})
|
||||
|
||||
go()
|
||||
}
|
||||
|
||||
export { startFreeTrial, upgradePlan }
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import App from '../base'
|
||||
import ExposedSettings from './exposed-settings'
|
||||
|
||||
App.constant('ExposedSettings', ExposedSettings)
|
|
@ -1,7 +1,3 @@
|
|||
import App from '../base'
|
||||
|
||||
const ExposedSettings = window.ExposedSettings
|
||||
|
||||
App.constant('ExposedSettings', ExposedSettings)
|
||||
|
||||
export default ExposedSettings
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import React, { createContext, useContext } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import ExposedSettings from '../../main/exposed-settings'
|
||||
|
||||
export const ApplicationContext = createContext()
|
||||
|
||||
export function ApplicationProvider({ children }) {
|
||||
const applicationContextValue = {
|
||||
user: window.user,
|
||||
exposedSettings: ExposedSettings
|
||||
}
|
||||
return (
|
||||
<ApplicationContext.Provider
|
||||
value={{
|
||||
user: window.user
|
||||
}}
|
||||
>
|
||||
<ApplicationContext.Provider value={applicationContextValue}>
|
||||
{children}
|
||||
</ApplicationContext.Provider>
|
||||
)
|
||||
|
@ -20,8 +21,6 @@ ApplicationProvider.propTypes = {
|
|||
}
|
||||
|
||||
export function useApplicationContext() {
|
||||
const { user } = useContext(ApplicationContext)
|
||||
return {
|
||||
user
|
||||
}
|
||||
const applicationContext = useContext(ApplicationContext)
|
||||
return applicationContext
|
||||
}
|
||||
|
|
|
@ -4,12 +4,18 @@ import PropTypes from 'prop-types'
|
|||
export const EditorContext = createContext()
|
||||
|
||||
export function EditorProvider({ children }) {
|
||||
const ownerId =
|
||||
window._ide.$scope.project && window._ide.$scope.project.owner
|
||||
? window._ide.$scope.project.owner._id
|
||||
: null
|
||||
|
||||
const editorContextValue = {
|
||||
projectId: window.project_id,
|
||||
isProjectOwner: ownerId === window.user.id
|
||||
}
|
||||
|
||||
return (
|
||||
<EditorContext.Provider
|
||||
value={{
|
||||
projectId: window.project_id
|
||||
}}
|
||||
>
|
||||
<EditorContext.Provider value={editorContextValue}>
|
||||
{children}
|
||||
</EditorContext.Provider>
|
||||
)
|
||||
|
@ -20,8 +26,6 @@ EditorProvider.propTypes = {
|
|||
}
|
||||
|
||||
export function useEditorContext() {
|
||||
const { projectId } = useContext(EditorContext)
|
||||
return {
|
||||
projectId
|
||||
}
|
||||
const editorContext = useContext(EditorContext)
|
||||
return editorContext
|
||||
}
|
||||
|
|
|
@ -76,6 +76,14 @@
|
|||
.btn-alert-variant(@ol-blue-gray-4);
|
||||
}
|
||||
|
||||
.log-entry-header-success {
|
||||
background-color: @green;
|
||||
}
|
||||
|
||||
.log-entry-header-link-success {
|
||||
.btn-alert-variant(@green);
|
||||
}
|
||||
|
||||
.log-entry-header-title,
|
||||
.log-entry-header-link {
|
||||
font-family: @font-family-sans-serif;
|
||||
|
|
|
@ -27,11 +27,9 @@ export function tearDownMathJaxStubs() {
|
|||
}
|
||||
|
||||
export function stubChatStore({ user }) {
|
||||
window._ide = { socket: { on: sinon.stub(), removeListener: sinon.stub() } }
|
||||
window.user = user
|
||||
}
|
||||
|
||||
export function tearDownChatStore() {
|
||||
delete window._ide
|
||||
delete window.user
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React from 'react'
|
||||
import { screen, render, fireEvent } from '@testing-library/react'
|
||||
import { screen, fireEvent } from '@testing-library/react'
|
||||
import PreviewLogsPane from '../../../../../frontend/js/features/preview/components/preview-logs-pane'
|
||||
import sinon from 'sinon'
|
||||
import { renderWithEditorContext } from '../../../helpers/render-with-context'
|
||||
|
||||
const { expect } = require('chai')
|
||||
|
||||
|
@ -60,7 +61,7 @@ entering extended mode
|
|||
const noOp = () =>
|
||||
describe('with logs', function() {
|
||||
beforeEach(function() {
|
||||
render(
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
logEntries={logEntries}
|
||||
rawLog={sampleRawLog}
|
||||
|
@ -125,7 +126,7 @@ entering extended mode
|
|||
}
|
||||
|
||||
it('renders a validation entry for known issues', function() {
|
||||
render(
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
validationIssues={sampleValidationIssues}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
|
@ -141,7 +142,7 @@ entering extended mode
|
|||
})
|
||||
|
||||
it('ignores unknown issues', function() {
|
||||
render(
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
validationIssues={{ unknownIssue: true }}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
|
@ -163,7 +164,7 @@ entering extended mode
|
|||
}
|
||||
|
||||
it('renders an error entry for known errors', function() {
|
||||
render(
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
errors={sampleErrors}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
|
@ -177,7 +178,7 @@ entering extended mode
|
|||
})
|
||||
|
||||
it('ignores unknown errors', function() {
|
||||
render(
|
||||
renderWithEditorContext(
|
||||
<PreviewLogsPane
|
||||
errors={{ unknownIssue: true }}
|
||||
onLogEntryLocationClick={onLogEntryLocationClick}
|
||||
|
|
|
@ -2,10 +2,27 @@ import React from 'react'
|
|||
import { render } from '@testing-library/react'
|
||||
import { ApplicationProvider } from '../../../frontend/js/shared/context/application-context'
|
||||
import { EditorProvider } from '../../../frontend/js/shared/context/editor-context'
|
||||
import sinon from 'sinon'
|
||||
|
||||
export function renderWithEditorContext(children, { user, projectId } = {}) {
|
||||
export function renderWithEditorContext(
|
||||
children,
|
||||
{ user = { id: '123abd' }, projectId } = {}
|
||||
) {
|
||||
window.user = user || window.user
|
||||
window.project_id = projectId != null ? projectId : window.project_id
|
||||
window._ide = {
|
||||
$scope: {
|
||||
project: {
|
||||
owner: {
|
||||
_id: '124abd'
|
||||
}
|
||||
}
|
||||
},
|
||||
socket: {
|
||||
on: sinon.stub(),
|
||||
removeListener: sinon.stub()
|
||||
}
|
||||
}
|
||||
return render(
|
||||
<ApplicationProvider>
|
||||
<EditorProvider>{children}</EditorProvider>
|
||||
|
|
Loading…
Reference in a new issue