mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 19:21:50 +00:00
Add unit tests for code_intelligence (#2396)
* Add unit tests for code_intelligence Adds a first set of jsdom-based tests for `handleCodeHost()`, testing the basic functionality of the browser extension (inject command palette, inject global debug palette, detect code views, decorate code views). To allow this, some refactoring was done so that the extension controller and platform context can be injected in `handleCodeHost()`. `showGlobalDebug` is now also injected, instead of directly accessing localStorage in `GlobalDebug.tsx`. This is just a starting point, these tests are not exhaustive. * use window * Clean up any * use mutation-observer polyfill * Properly mock platformContext * Move mutation-observer module declaration into the types folder
This commit is contained in:
parent
c56753b88e
commit
3da03efe69
@ -0,0 +1,300 @@
|
||||
const RENDER = jest.fn()
|
||||
jest.mock('react-dom', () => ({
|
||||
createPortal: jest.fn(el => el),
|
||||
render: RENDER,
|
||||
unmountComponentAtNode: jest.fn(),
|
||||
}))
|
||||
|
||||
import { uniqueId } from 'lodash'
|
||||
import MutationObserver from 'mutation-observer'
|
||||
import renderer from 'react-test-renderer'
|
||||
import { from, NEVER, of, Subject, Subscription } from 'rxjs'
|
||||
import { filter, map, skip, switchMap, take } from 'rxjs/operators'
|
||||
import { Services } from '../../../../../shared/src/api/client/services'
|
||||
import { Range } from '../../../../../shared/src/api/extension/types/range'
|
||||
import { integrationTestContext } from '../../../../../shared/src/api/integration-test/testHelpers'
|
||||
import { Controller } from '../../../../../shared/src/extensions/controller'
|
||||
import { PlatformContextProps } from '../../../../../shared/src/platform/context'
|
||||
import { isDefined } from '../../../../../shared/src/util/types'
|
||||
import { FileInfo, handleCodeHost } from './code_intelligence'
|
||||
|
||||
const elementRenderedAtMount = (mount: Element): renderer.ReactTestRendererJSON | undefined => {
|
||||
const call = RENDER.mock.calls.find(call => call[1] === mount)
|
||||
return call && call[0]
|
||||
}
|
||||
|
||||
jest.mock('uuid', () => ({
|
||||
v4: () => 'uuid',
|
||||
}))
|
||||
|
||||
const createMockController = (services: Services): Controller => ({
|
||||
services,
|
||||
notifications: NEVER,
|
||||
executeCommand: jest.fn(),
|
||||
unsubscribe: jest.fn(),
|
||||
})
|
||||
|
||||
const createMockPlatformContext = (
|
||||
partialMocks?: Partial<PlatformContextProps<'forceUpdateTooltip' | 'sideloadedExtensionURL' | 'urlToFile'>>
|
||||
): PlatformContextProps<'forceUpdateTooltip' | 'sideloadedExtensionURL' | 'urlToFile'> => ({
|
||||
platformContext: {
|
||||
forceUpdateTooltip: jest.fn(),
|
||||
urlToFile: jest.fn(),
|
||||
sideloadedExtensionURL: new Subject<string | null>(),
|
||||
...partialMocks,
|
||||
},
|
||||
})
|
||||
|
||||
describe('handleCodeHost()', () => {
|
||||
beforeAll(() => {
|
||||
// jsdom doesn't support MutationObserver or IntersectionObserver, so we need to mock them
|
||||
;(window as any).MutationObserver = MutationObserver
|
||||
;(window as any).IntersectionObserver = class {
|
||||
constructor(
|
||||
private callback: (entries: Pick<IntersectionObserverEntry, 'target' | 'isIntersecting'>[]) => void
|
||||
) {}
|
||||
public observe = (el: Element) => setTimeout(() => this.callback([{ isIntersecting: true, target: el }]), 0)
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
delete (window as any).MutationObserver
|
||||
delete (window as any).IntersectionObserver
|
||||
})
|
||||
|
||||
let subscriptions = new Subscription()
|
||||
|
||||
afterEach(() => {
|
||||
for (const el of document.querySelectorAll('.test')) {
|
||||
el.remove()
|
||||
}
|
||||
RENDER.mockClear()
|
||||
subscriptions.unsubscribe()
|
||||
subscriptions = new Subscription()
|
||||
})
|
||||
|
||||
const createTestElement = () => {
|
||||
const el = document.createElement('div')
|
||||
el.className = `test test-${uniqueId()}`
|
||||
document.body.appendChild(el)
|
||||
return el
|
||||
}
|
||||
|
||||
test('renders the hoverlay container', async () => {
|
||||
const { services } = await integrationTestContext()
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: false,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const overlayMount = document.body.firstChild! as HTMLElement
|
||||
expect(overlayMount.className).toBe('overlay-mount-container')
|
||||
const renderedOverlay = elementRenderedAtMount(overlayMount)
|
||||
expect(renderedOverlay).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('renders the command palette if codeHost.getCommandPaletteMount is defined', async () => {
|
||||
const { services } = await integrationTestContext()
|
||||
const commandPaletteMount = createTestElement()
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
getCommandPaletteMount: () => commandPaletteMount,
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: false,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const renderedCommandPalette = elementRenderedAtMount(commandPaletteMount)
|
||||
expect(renderedCommandPalette).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('creates a .global-debug element and renders the debug palette if showGlobalDebug is true', async () => {
|
||||
const { services } = await integrationTestContext()
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: true,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const globalDebugMount = document.querySelector('.global-debug')
|
||||
expect(globalDebugMount).not.toBeUndefined()
|
||||
const renderedDebugElement = elementRenderedAtMount(globalDebugMount!)
|
||||
expect(renderedDebugElement).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('renders the debug palette to the provided mount if codeHost.globalDebugMount is defined', async () => {
|
||||
const { services } = await integrationTestContext()
|
||||
const globalDebugMount = createTestElement()
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
getGlobalDebugMount: () => globalDebugMount,
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: true,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const renderedDebugElement = elementRenderedAtMount(globalDebugMount)
|
||||
expect(renderedDebugElement).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('detects code views based on selectors', async () => {
|
||||
const { services } = await integrationTestContext()
|
||||
const codeView = createTestElement()
|
||||
codeView.id = 'code'
|
||||
const toolbarMount = document.createElement('div')
|
||||
codeView.appendChild(toolbarMount)
|
||||
const fileInfo: FileInfo = {
|
||||
repoName: 'foo',
|
||||
filePath: '/bar.ts',
|
||||
commitID: '1',
|
||||
}
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
codeViews: [
|
||||
{
|
||||
selector: `#code`,
|
||||
dom: {
|
||||
getCodeElementFromTarget: jest.fn(),
|
||||
getCodeElementFromLineNumber: jest.fn(),
|
||||
getLineNumberFromCodeElement: jest.fn(),
|
||||
},
|
||||
resolveFileInfo: codeView => of(fileInfo),
|
||||
getToolbarMount: () => toolbarMount,
|
||||
},
|
||||
],
|
||||
selectionsChanges: () => of([]),
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: true,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const viewComponents = await from(services.model.model)
|
||||
.pipe(
|
||||
skip(1),
|
||||
take(1),
|
||||
map(({ visibleViewComponents }) => visibleViewComponents)
|
||||
)
|
||||
.toPromise()
|
||||
expect(viewComponents).toEqual([
|
||||
{
|
||||
isActive: true,
|
||||
item: {
|
||||
languageId: 'typescript',
|
||||
text: undefined,
|
||||
uri: 'git://foo?1#/bar.ts',
|
||||
},
|
||||
selections: [],
|
||||
type: 'textEditor',
|
||||
},
|
||||
])
|
||||
expect(codeView.classList.contains('sg-mounted')).toBe(true)
|
||||
const toolbar = elementRenderedAtMount(toolbarMount)
|
||||
expect(toolbar).not.toBeUndefined()
|
||||
})
|
||||
|
||||
test('decorates a code view', async () => {
|
||||
const { extensionAPI, services } = await integrationTestContext(undefined, {
|
||||
roots: [],
|
||||
visibleViewComponents: [],
|
||||
})
|
||||
const codeView = createTestElement()
|
||||
codeView.id = 'code'
|
||||
const fileInfo: FileInfo = {
|
||||
repoName: 'foo',
|
||||
filePath: '/bar.ts',
|
||||
commitID: '1',
|
||||
}
|
||||
const line = document.createElement('div')
|
||||
codeView.appendChild(line)
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost: {
|
||||
name: 'test',
|
||||
check: () => true,
|
||||
codeViews: [
|
||||
{
|
||||
selector: `#code`,
|
||||
dom: {
|
||||
getCodeElementFromTarget: jest.fn(),
|
||||
getCodeElementFromLineNumber: () => line,
|
||||
getLineNumberFromCodeElement: jest.fn(),
|
||||
},
|
||||
resolveFileInfo: codeView => of(fileInfo),
|
||||
},
|
||||
],
|
||||
selectionsChanges: () => of([]),
|
||||
},
|
||||
extensionsController: createMockController(services),
|
||||
showGlobalDebug: true,
|
||||
...createMockPlatformContext(),
|
||||
})
|
||||
)
|
||||
const activeEditor = await from(extensionAPI.app.activeWindowChanges)
|
||||
.pipe(
|
||||
filter(isDefined),
|
||||
switchMap(window => window.activeViewComponentChanges),
|
||||
filter(isDefined),
|
||||
take(1)
|
||||
)
|
||||
.toPromise()
|
||||
const decorationType = extensionAPI.app.createDecorationType()
|
||||
const decorated = () =>
|
||||
services.textDocumentDecoration
|
||||
.getDecorations({ uri: 'git://foo?1#/bar.ts' })
|
||||
.pipe(
|
||||
filter(decorations => decorations !== []),
|
||||
take(1)
|
||||
)
|
||||
.toPromise()
|
||||
|
||||
// Set decorations and verify that a decoration attachment has been added
|
||||
activeEditor.setDecorations(decorationType, [
|
||||
{
|
||||
range: new Range(0, 0, 0, 0),
|
||||
after: {
|
||||
contentText: 'test decoration',
|
||||
},
|
||||
},
|
||||
])
|
||||
await decorated()
|
||||
expect(line.querySelectorAll('.line-decoration-attachment').length).toBe(1)
|
||||
expect(line.querySelector('.line-decoration-attachment')!.textContent).toEqual('test decoration')
|
||||
|
||||
// Decorate the code view again, and verify that previous decorations
|
||||
// are cleaned up and replaced by the new decorations.
|
||||
activeEditor.setDecorations(decorationType, [
|
||||
{
|
||||
range: new Range(0, 0, 0, 0),
|
||||
after: {
|
||||
contentText: 'test decoration 2',
|
||||
},
|
||||
},
|
||||
])
|
||||
await decorated()
|
||||
expect(line.querySelectorAll('.line-decoration-attachment').length).toBe(1)
|
||||
expect(line.querySelector('.line-decoration-attachment')!.textContent).toEqual('test decoration 2')
|
||||
})
|
||||
})
|
||||
@ -31,7 +31,7 @@ import { registerHighlightContributions } from '../../../../../shared/src/highli
|
||||
import { ActionItemProps } from '../../../../../shared/src/actions/ActionItem'
|
||||
import { Model, ViewComponentData } from '../../../../../shared/src/api/client/model'
|
||||
import { HoverMerged } from '../../../../../shared/src/api/client/types/hover'
|
||||
import { ExtensionsControllerProps } from '../../../../../shared/src/extensions/controller'
|
||||
import { Controller } from '../../../../../shared/src/extensions/controller'
|
||||
import { getHoverActions, registerHoverContributions } from '../../../../../shared/src/hover/actions'
|
||||
import { HoverContext, HoverOverlay } from '../../../../../shared/src/hover/HoverOverlay'
|
||||
import { getModeFromPath } from '../../../../../shared/src/languages'
|
||||
@ -61,7 +61,7 @@ import { githubCodeHost } from '../github/code_intelligence'
|
||||
import { gitlabCodeHost } from '../gitlab/code_intelligence'
|
||||
import { phabricatorCodeHost } from '../phabricator/code_intelligence'
|
||||
import { fetchFileContents, findCodeViews } from './code_views'
|
||||
import { applyDecorations, initializeExtensions } from './extensions'
|
||||
import { applyDecorations, initializeExtensions, injectCommandPalette, injectGlobalDebug } from './extensions'
|
||||
import { injectViewContextOnSourcegraph } from './external_links'
|
||||
|
||||
registerHighlightContributions()
|
||||
@ -162,7 +162,7 @@ export interface CodeHost {
|
||||
|
||||
/**
|
||||
* Resolve `CodeView`s from the DOM. This is useful when each code view type
|
||||
* doesn't have a distinct selector for
|
||||
* doesn't have a distinct selector
|
||||
*/
|
||||
codeViewResolver?: CodeViewResolver
|
||||
|
||||
@ -239,23 +239,24 @@ export interface FileInfo {
|
||||
baseContent?: string
|
||||
}
|
||||
|
||||
interface CodeIntelligenceProps
|
||||
extends PlatformContextProps<'forceUpdateTooltip' | 'urlToFile' | 'sideloadedExtensionURL'> {
|
||||
codeHost: CodeHost
|
||||
extensionsController: Controller
|
||||
showGlobalDebug?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the page for code intelligence. It creates the hoverifier, injects
|
||||
* and mounts the hover overlay and then returns the hoverifier.
|
||||
*
|
||||
* @param codeHost
|
||||
*/
|
||||
function initCodeIntelligence(
|
||||
codeHost: CodeHost
|
||||
): {
|
||||
hoverifier: Hoverifier<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec, HoverMerged, ActionItemProps>
|
||||
controllers: ExtensionsControllerProps & PlatformContextProps
|
||||
} {
|
||||
const {
|
||||
platformContext,
|
||||
extensionsController,
|
||||
}: PlatformContextProps & ExtensionsControllerProps = initializeExtensions(codeHost)
|
||||
|
||||
export function initCodeIntelligence({
|
||||
codeHost,
|
||||
platformContext,
|
||||
extensionsController,
|
||||
}: CodeIntelligenceProps): Hoverifier<RepoSpec & RevSpec & FileSpec & ResolvedRevSpec, HoverMerged, ActionItemProps> {
|
||||
const { getHover } = createLSPFromExtensions(extensionsController)
|
||||
|
||||
/** Emits when the close button was clicked */
|
||||
@ -398,7 +399,7 @@ function initCodeIntelligence(
|
||||
overlayContainerMount
|
||||
)
|
||||
|
||||
return { hoverifier, controllers: { platformContext, extensionsController } }
|
||||
return hoverifier
|
||||
}
|
||||
|
||||
/**
|
||||
@ -410,16 +411,15 @@ export interface ResolvedCodeView extends CodeViewWithOutSelector {
|
||||
codeView: HTMLElement
|
||||
}
|
||||
|
||||
function handleCodeHost(codeHost: CodeHost): Subscription {
|
||||
const {
|
||||
hoverifier,
|
||||
controllers: { platformContext, extensionsController },
|
||||
} = initCodeIntelligence(codeHost)
|
||||
|
||||
export function handleCodeHost({
|
||||
codeHost,
|
||||
extensionsController,
|
||||
platformContext,
|
||||
showGlobalDebug,
|
||||
}: CodeIntelligenceProps): Subscription {
|
||||
const history = H.createBrowserHistory()
|
||||
const subscriptions = new Subscription()
|
||||
|
||||
subscriptions.add(hoverifier)
|
||||
|
||||
const ensureRepoExists = (context: CodeHostContext) =>
|
||||
resolveRev(context).pipe(
|
||||
retryWhenCloneInProgressError(),
|
||||
@ -439,6 +439,18 @@ function handleCodeHost(codeHost: CodeHost): Subscription {
|
||||
})
|
||||
}
|
||||
|
||||
const hoverifier = initCodeIntelligence({ codeHost, extensionsController, platformContext, showGlobalDebug })
|
||||
subscriptions.add(hoverifier)
|
||||
|
||||
// Inject UI components
|
||||
injectCommandPalette({ extensionsController, platformContext, history, getMount: codeHost.getCommandPaletteMount })
|
||||
injectGlobalDebug({
|
||||
extensionsController,
|
||||
platformContext,
|
||||
getMount: codeHost.getGlobalDebugMount,
|
||||
history,
|
||||
showGlobalDebug,
|
||||
})
|
||||
injectViewContextOnSourcegraph(sourcegraphUrl, codeHost, ensureRepoExists, isInPage ? undefined : openOptionsMenu)
|
||||
|
||||
// A stream of selections for the current code view. By default, selections
|
||||
@ -586,13 +598,27 @@ function handleCodeHost(codeHost: CodeHost): Subscription {
|
||||
return subscriptions
|
||||
}
|
||||
|
||||
async function injectCodeIntelligenceToCodeHosts(codeHosts: CodeHost[]): Promise<Subscription> {
|
||||
const SHOW_DEBUG = () => localStorage.getItem('debug') !== null
|
||||
|
||||
export async function injectCodeIntelligenceToCodeHosts(
|
||||
codeHosts: CodeHost[],
|
||||
showGlobalDebug = SHOW_DEBUG()
|
||||
): Promise<Subscription> {
|
||||
const subscriptions = new Subscription()
|
||||
|
||||
for (const codeHost of codeHosts) {
|
||||
const isCodeHost = await Promise.resolve(codeHost.check())
|
||||
if (isCodeHost) {
|
||||
subscriptions.add(handleCodeHost(codeHost))
|
||||
const { platformContext, extensionsController } = initializeExtensions(codeHost)
|
||||
subscriptions.add(extensionsController)
|
||||
subscriptions.add(
|
||||
handleCodeHost({
|
||||
codeHost,
|
||||
extensionsController,
|
||||
platformContext,
|
||||
showGlobalDebug,
|
||||
})
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,19 +27,24 @@ import { CodeHost } from './code_intelligence'
|
||||
/**
|
||||
* Initializes extensions for a page. It creates the {@link PlatformContext} and extensions controller.
|
||||
*
|
||||
* If the "Use extensions" feature flag is enabled (or always for Sourcegraph.com), it injects the command palette.
|
||||
* If extensions are not supported by the associated Sourcegraph instance, the extensions controller will behave as
|
||||
* though no individual extensions are enabled, which makes it effectively a noop.
|
||||
*/
|
||||
export function initializeExtensions({
|
||||
getCommandPaletteMount,
|
||||
urlToFile,
|
||||
}: Pick<CodeHost, 'getCommandPaletteMount' | 'urlToFile'>): PlatformContextProps & ExtensionsControllerProps {
|
||||
}: Pick<CodeHost, 'urlToFile'>): PlatformContextProps & ExtensionsControllerProps {
|
||||
const platformContext = createPlatformContext({ urlToFile })
|
||||
const extensionsController = createExtensionsController(platformContext)
|
||||
const history = H.createBrowserHistory()
|
||||
return { platformContext, extensionsController }
|
||||
}
|
||||
|
||||
if (getCommandPaletteMount) {
|
||||
interface InjectProps
|
||||
extends PlatformContextProps<'forceUpdateTooltip' | 'sideloadedExtensionURL'>,
|
||||
ExtensionsControllerProps {
|
||||
getMount?: () => HTMLElement
|
||||
history: H.History
|
||||
}
|
||||
|
||||
export function injectCommandPalette({ extensionsController, platformContext, getMount, history }: InjectProps): void {
|
||||
if (getMount) {
|
||||
render(
|
||||
<ShortcutProvider>
|
||||
<TelemetryContext.Provider value={eventLogger}>
|
||||
@ -52,20 +57,28 @@ export function initializeExtensions({
|
||||
<Notifications extensionsController={extensionsController} />
|
||||
</TelemetryContext.Provider>
|
||||
</ShortcutProvider>,
|
||||
getCommandPaletteMount()
|
||||
getMount()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render(
|
||||
<GlobalDebug
|
||||
extensionsController={extensionsController}
|
||||
location={history.location}
|
||||
platformContext={platformContext}
|
||||
/>,
|
||||
getGlobalDebugMount()
|
||||
)
|
||||
|
||||
return { platformContext, extensionsController }
|
||||
export function injectGlobalDebug({
|
||||
extensionsController,
|
||||
platformContext,
|
||||
history,
|
||||
showGlobalDebug,
|
||||
getMount = getGlobalDebugMount,
|
||||
}: InjectProps & { showGlobalDebug?: boolean }): void {
|
||||
if (showGlobalDebug) {
|
||||
render(
|
||||
<GlobalDebug
|
||||
extensionsController={extensionsController}
|
||||
location={history.location}
|
||||
platformContext={platformContext}
|
||||
/>,
|
||||
getMount()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const IS_LIGHT_THEME = true // assume all code hosts have a light theme (correct for now)
|
||||
|
||||
@ -19,7 +19,7 @@ export interface ButtonProps {
|
||||
iconStyle?: React.CSSProperties
|
||||
}
|
||||
|
||||
interface CodeViewToolbarProps extends PlatformContextProps, ExtensionsControllerProps, FileInfo {
|
||||
interface CodeViewToolbarProps extends PlatformContextProps<'forceUpdateTooltip'>, ExtensionsControllerProps, FileInfo {
|
||||
onEnabledChange?: (enabled: boolean) => void
|
||||
|
||||
buttonProps: ButtonProps
|
||||
|
||||
@ -6,13 +6,11 @@ import { PlatformContextProps } from '../../../../../shared/src/platform/context
|
||||
import { sourcegraphUrl } from '../util/context'
|
||||
import { ShortcutProvider } from './ShortcutProvider'
|
||||
|
||||
interface Props extends PlatformContextProps {
|
||||
interface Props extends PlatformContextProps<'sideloadedExtensionURL'> {
|
||||
location: H.Location
|
||||
extensionsController: ClientController
|
||||
}
|
||||
|
||||
const SHOW_DEBUG = localStorage.getItem('debug') !== null
|
||||
|
||||
const ExtensionLink: React.FunctionComponent<{ id: string }> = props => {
|
||||
const extensionURL = new URL(sourcegraphUrl)
|
||||
extensionURL.pathname = `extensions/${props.id}`
|
||||
@ -22,20 +20,19 @@ const ExtensionLink: React.FunctionComponent<{ id: string }> = props => {
|
||||
/**
|
||||
* A global debug toolbar shown in the bottom right of the window.
|
||||
*/
|
||||
export const GlobalDebug: React.FunctionComponent<Props> = props =>
|
||||
SHOW_DEBUG ? (
|
||||
<div className="global-debug navbar navbar-expand">
|
||||
<div className="navbar-nav align-items-center">
|
||||
<div className="nav-item">
|
||||
<ShortcutProvider>
|
||||
<ExtensionStatusPopover
|
||||
location={props.location}
|
||||
extensionsController={props.extensionsController}
|
||||
link={ExtensionLink}
|
||||
platformContext={props.platformContext}
|
||||
/>
|
||||
</ShortcutProvider>
|
||||
</div>
|
||||
export const GlobalDebug: React.FunctionComponent<Props> = props => (
|
||||
<div className="global-debug navbar navbar-expand">
|
||||
<div className="navbar-nav align-items-center">
|
||||
<div className="nav-item">
|
||||
<ShortcutProvider>
|
||||
<ExtensionStatusPopover
|
||||
location={props.location}
|
||||
extensionsController={props.extensionsController}
|
||||
link={ExtensionLink}
|
||||
platformContext={props.platformContext}
|
||||
/>
|
||||
</ShortcutProvider>
|
||||
</div>
|
||||
</div>
|
||||
) : null
|
||||
</div>
|
||||
)
|
||||
|
||||
6
client/browser/src/types/mutation-observer/index.d.ts
vendored
Normal file
6
client/browser/src/types/mutation-observer/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* A MutationObserver polyfill
|
||||
*/
|
||||
declare module 'mutation-observer' {
|
||||
const MutationObserver: MutationObserver
|
||||
}
|
||||
@ -18,11 +18,18 @@ const config = {
|
||||
'/node_modules/(?!abortable-rx|@sourcegraph/react-loading-spinner|@sourcegraph/codeintellify|@sourcegraph/comlink)',
|
||||
],
|
||||
|
||||
moduleNameMapper: { '\\.s?css$': 'identity-obj-proxy' },
|
||||
moduleNameMapper: { '\\.s?css$': 'identity-obj-proxy', '^worker-loader': 'identity-obj-proxy' },
|
||||
|
||||
// By default, don't clutter `yarn test --watch` output with the full coverage table. To see it, use the
|
||||
// `--coverageReporters text` jest option.
|
||||
coverageReporters: ['json', 'lcov', 'text-summary'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
diagnostics: {
|
||||
pathRegex: '(client/browser|shared|web)/src',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = config
|
||||
|
||||
@ -142,6 +142,7 @@
|
||||
"message-port-polyfill": "^0.1.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"mkdirp-promise": "^5.0.1",
|
||||
"mutation-observer": "^1.0.3",
|
||||
"mz": "^2.7.0",
|
||||
"node-sass": "^4.11.0",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.1",
|
||||
|
||||
@ -10,7 +10,7 @@ import { ExtensionsControllerProps } from '../extensions/controller'
|
||||
import { PlatformContextProps } from '../platform/context'
|
||||
import { asError, ErrorLike, isErrorLike } from '../util/errors'
|
||||
|
||||
interface Props extends ExtensionsControllerProps, PlatformContextProps {
|
||||
interface Props extends ExtensionsControllerProps, PlatformContextProps<'sideloadedExtensionURL'> {
|
||||
link: React.ComponentType<{ id: string }>
|
||||
}
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ const LOADING: 'loading' = 'loading'
|
||||
* "Find references".
|
||||
*/
|
||||
export function getHoverActions(
|
||||
{ extensionsController, platformContext }: ExtensionsControllerProps & PlatformContextProps,
|
||||
{ extensionsController, platformContext }: ExtensionsControllerProps & PlatformContextProps<'urlToFile'>,
|
||||
hoverContext: HoveredToken & HoverContext
|
||||
): Observable<ActionItemProps[]> {
|
||||
return getHoverActionsContext({ extensionsController, platformContext }, hoverContext).pipe(
|
||||
@ -71,7 +71,7 @@ export function getHoverActionsContext(
|
||||
extensionsController,
|
||||
platformContext: { urlToFile },
|
||||
}:
|
||||
| (ExtensionsControllerProps & PlatformContextProps)
|
||||
| (ExtensionsControllerProps & PlatformContextProps<'urlToFile'>)
|
||||
| {
|
||||
extensionsController: {
|
||||
services: {
|
||||
|
||||
@ -10464,6 +10464,11 @@ multicast-dns@^6.0.1:
|
||||
dns-packet "^1.3.1"
|
||||
thunky "^1.0.2"
|
||||
|
||||
mutation-observer@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/mutation-observer/-/mutation-observer-1.0.3.tgz#42e9222b101bca82e5ba9d5a7acf4a14c0f263d0"
|
||||
integrity sha512-M/O/4rF2h776hV7qGMZUH3utZLO/jK7p8rnNgGkjKUw8zCGjRQPxB8z6+5l8+VjRUQ3dNYu4vjqXYLr+U8ZVNA==
|
||||
|
||||
mute-stdout@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user