-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15611 from getsentry/prepare-release/9.5.0
meta(changelog): Update changelog for 9.5.0
- Loading branch information
Showing
32 changed files
with
1,452 additions
and
418 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { type Integration } from '@sentry/core'; | ||
import * as sentryNode from '@sentry/node'; | ||
import type { Mock } from 'bun:test'; | ||
import { afterEach, beforeEach, describe, it, spyOn, mock, expect } from 'bun:test'; | ||
import { getClient, init } from '../src'; | ||
|
||
const PUBLIC_DSN = 'https://username@domain/123'; | ||
|
||
class MockIntegration implements Integration { | ||
public name: string; | ||
public setupOnce: Mock<() => void>; | ||
public constructor(name: string) { | ||
this.name = name; | ||
this.setupOnce = mock(() => undefined); | ||
} | ||
} | ||
|
||
describe('init()', () => { | ||
let mockAutoPerformanceIntegrations: Mock<() => Integration[]>; | ||
|
||
beforeEach(() => { | ||
// @ts-expect-error weird | ||
mockAutoPerformanceIntegrations = spyOn(sentryNode, 'getAutoPerformanceIntegrations'); | ||
}); | ||
|
||
afterEach(() => { | ||
mockAutoPerformanceIntegrations.mockRestore(); | ||
}); | ||
|
||
describe('integrations', () => { | ||
it("doesn't install default integrations if told not to", () => { | ||
init({ dsn: PUBLIC_DSN, defaultIntegrations: false }); | ||
|
||
const client = getClient(); | ||
|
||
expect(client?.getOptions().integrations).toEqual([]); | ||
|
||
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('installs merged default integrations, with overrides provided through options', () => { | ||
const mockDefaultIntegrations = [ | ||
new MockIntegration('Some mock integration 2.1'), | ||
new MockIntegration('Some mock integration 2.2'), | ||
]; | ||
|
||
const mockIntegrations = [ | ||
new MockIntegration('Some mock integration 2.1'), | ||
new MockIntegration('Some mock integration 2.3'), | ||
]; | ||
|
||
init({ dsn: PUBLIC_DSN, integrations: mockIntegrations, defaultIntegrations: mockDefaultIntegrations }); | ||
|
||
expect(mockDefaultIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(0); | ||
expect(mockDefaultIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('installs integrations returned from a callback function', () => { | ||
const mockDefaultIntegrations = [ | ||
new MockIntegration('Some mock integration 3.1'), | ||
new MockIntegration('Some mock integration 3.2'), | ||
]; | ||
|
||
const newIntegration = new MockIntegration('Some mock integration 3.3'); | ||
|
||
init({ | ||
dsn: PUBLIC_DSN, | ||
defaultIntegrations: mockDefaultIntegrations, | ||
integrations: integrations => { | ||
const newIntegrations = [...integrations]; | ||
newIntegrations[1] = newIntegration; | ||
return newIntegrations; | ||
}, | ||
}); | ||
|
||
expect(mockDefaultIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockDefaultIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(0); | ||
expect(newIntegration.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0); | ||
}); | ||
|
||
it('installs performance default instrumentations if tracing is enabled', () => { | ||
const autoPerformanceIntegrations = [new MockIntegration('Performance integration')]; | ||
mockAutoPerformanceIntegrations.mockImplementation(() => autoPerformanceIntegrations); | ||
|
||
const mockIntegrations = [ | ||
new MockIntegration('Some mock integration 4.1'), | ||
new MockIntegration('Some mock integration 4.3'), | ||
]; | ||
|
||
init({ | ||
dsn: PUBLIC_DSN, | ||
integrations: mockIntegrations, | ||
tracesSampleRate: 1, | ||
}); | ||
|
||
expect(mockIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockIntegrations[1]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(autoPerformanceIntegrations[0]?.setupOnce).toHaveBeenCalledTimes(1); | ||
expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(1); | ||
|
||
const integrations = getClient()?.getOptions().integrations; | ||
expect(integrations).toBeArray(); | ||
expect(integrations?.map(({ name }) => name)).toContain('Performance integration'); | ||
expect(integrations?.map(({ name }) => name)).toContain('Some mock integration 4.1'); | ||
expect(integrations?.map(({ name }) => name)).toContain('Some mock integration 4.3'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
91 changes: 91 additions & 0 deletions
91
packages/feedback/src/screenshot/components/Annotations.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import type { VNode, h as hType } from 'preact'; | ||
import type * as Hooks from 'preact/hooks'; | ||
import { DOCUMENT } from '../../constants'; | ||
|
||
interface FactoryParams { | ||
h: typeof hType; | ||
} | ||
|
||
export default function AnnotationsFactory({ | ||
h, // eslint-disable-line @typescript-eslint/no-unused-vars | ||
}: FactoryParams) { | ||
return function Annotations({ | ||
action, | ||
imageBuffer, | ||
annotatingRef, | ||
}: { | ||
action: 'crop' | 'annotate' | ''; | ||
imageBuffer: HTMLCanvasElement; | ||
annotatingRef: Hooks.Ref<HTMLCanvasElement>; | ||
}): VNode { | ||
const onAnnotateStart = (): void => { | ||
if (action !== 'annotate') { | ||
return; | ||
} | ||
|
||
const handleMouseMove = (moveEvent: MouseEvent): void => { | ||
const annotateCanvas = annotatingRef.current; | ||
if (annotateCanvas) { | ||
const rect = annotateCanvas.getBoundingClientRect(); | ||
const x = moveEvent.clientX - rect.x; | ||
const y = moveEvent.clientY - rect.y; | ||
|
||
const ctx = annotateCanvas.getContext('2d'); | ||
if (ctx) { | ||
ctx.lineTo(x, y); | ||
ctx.stroke(); | ||
ctx.beginPath(); | ||
ctx.moveTo(x, y); | ||
} | ||
} | ||
}; | ||
|
||
const handleMouseUp = (): void => { | ||
const ctx = annotatingRef.current?.getContext('2d'); | ||
if (ctx) { | ||
ctx.beginPath(); | ||
} | ||
|
||
// Add your apply annotation logic here | ||
applyAnnotation(); | ||
|
||
DOCUMENT.removeEventListener('mousemove', handleMouseMove); | ||
DOCUMENT.removeEventListener('mouseup', handleMouseUp); | ||
}; | ||
|
||
DOCUMENT.addEventListener('mousemove', handleMouseMove); | ||
DOCUMENT.addEventListener('mouseup', handleMouseUp); | ||
}; | ||
|
||
const applyAnnotation = (): void => { | ||
// Logic to apply the annotation | ||
const imageCtx = imageBuffer.getContext('2d'); | ||
const annotateCanvas = annotatingRef.current; | ||
if (imageCtx && annotateCanvas) { | ||
imageCtx.drawImage( | ||
annotateCanvas, | ||
0, | ||
0, | ||
annotateCanvas.width, | ||
annotateCanvas.height, | ||
0, | ||
0, | ||
imageBuffer.width, | ||
imageBuffer.height, | ||
); | ||
|
||
const annotateCtx = annotateCanvas.getContext('2d'); | ||
if (annotateCtx) { | ||
annotateCtx.clearRect(0, 0, annotateCanvas.width, annotateCanvas.height); | ||
} | ||
} | ||
}; | ||
return ( | ||
<canvas | ||
class={`editor__annotation ${action === 'annotate' ? 'editor__annotation--active' : ''}`} | ||
onMouseDown={onAnnotateStart} | ||
ref={annotatingRef} | ||
></canvas> | ||
); | ||
}; | ||
} |
Oops, something went wrong.