|
|
@@ -14,6 +14,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('renders an indicator with an upward Material Symbol for direction "above"', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -29,6 +30,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('renders an indicator with a downward Material Symbol for direction "below"', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'below',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -43,6 +45,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('places the arrow before the avatar for direction "above"', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: undefined,
|
|
|
@@ -56,6 +59,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('places the avatar before the arrow for direction "below"', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'below',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: undefined,
|
|
|
@@ -69,6 +73,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('renders an avatar image when imageUrlCached is provided', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -85,6 +90,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('renders initials fallback when imageUrlCached is undefined', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: undefined,
|
|
|
@@ -102,6 +108,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('applies cm-yRichCursorActive class when isActive is true', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -114,6 +121,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('does NOT apply cm-yRichCursorActive class when isActive is false', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -126,6 +134,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('applies border-color on the indicator wrapper from the color parameter', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: undefined,
|
|
|
@@ -138,6 +147,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('sets borderColor on the avatar img to the cursor color', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#ff0000',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/avatar.png',
|
|
|
@@ -153,6 +163,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('sets borderColor on the initials element to the cursor color', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'above',
|
|
|
+ clientId: 1,
|
|
|
color: '#0000ff',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: undefined,
|
|
|
@@ -168,6 +179,7 @@ describe('createOffScreenIndicator', () => {
|
|
|
it('sets borderColor on the onerror-fallback initials to the cursor color', () => {
|
|
|
const el = createOffScreenIndicator({
|
|
|
direction: 'below',
|
|
|
+ clientId: 1,
|
|
|
color: '#00ff00',
|
|
|
name: 'Alice',
|
|
|
imageUrlCached: '/broken.png',
|
|
|
@@ -183,3 +195,83 @@ describe('createOffScreenIndicator', () => {
|
|
|
expect(initials?.style.borderColor).toBe('#00ff00');
|
|
|
});
|
|
|
});
|
|
|
+
|
|
|
+/**
|
|
|
+ * Task 20.1 — Click behavior tests for off-screen indicators
|
|
|
+ * Requirements: 6.6, 6.7
|
|
|
+ */
|
|
|
+describe('createOffScreenIndicator — click behavior (Task 20.1)', () => {
|
|
|
+ it('applies cursor: pointer on the indicator root when onClick is provided', () => {
|
|
|
+ const el = createOffScreenIndicator({
|
|
|
+ direction: 'above',
|
|
|
+ clientId: 42,
|
|
|
+ color: '#ff0000',
|
|
|
+ name: 'Alice',
|
|
|
+ imageUrlCached: undefined,
|
|
|
+ isActive: false,
|
|
|
+ onClick: vi.fn(),
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(el.style.cursor).toBe('pointer');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('does NOT apply cursor: pointer when onClick is not provided', () => {
|
|
|
+ const el = createOffScreenIndicator({
|
|
|
+ direction: 'above',
|
|
|
+ clientId: 42,
|
|
|
+ color: '#ff0000',
|
|
|
+ name: 'Alice',
|
|
|
+ imageUrlCached: undefined,
|
|
|
+ isActive: false,
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(el.style.cursor).toBe('');
|
|
|
+ });
|
|
|
+
|
|
|
+ it('calls onClick with the correct clientId when indicator is clicked', () => {
|
|
|
+ const onClick = vi.fn();
|
|
|
+ const el = createOffScreenIndicator({
|
|
|
+ direction: 'above',
|
|
|
+ clientId: 42,
|
|
|
+ color: '#ff0000',
|
|
|
+ name: 'Alice',
|
|
|
+ imageUrlCached: undefined,
|
|
|
+ isActive: false,
|
|
|
+ onClick,
|
|
|
+ });
|
|
|
+
|
|
|
+ el.dispatchEvent(new Event('click'));
|
|
|
+
|
|
|
+ expect(onClick).toHaveBeenCalledOnce();
|
|
|
+ expect(onClick).toHaveBeenCalledWith(42);
|
|
|
+ });
|
|
|
+
|
|
|
+ it('does not throw when clicked and onClick is not provided', () => {
|
|
|
+ const el = createOffScreenIndicator({
|
|
|
+ direction: 'below',
|
|
|
+ clientId: 99,
|
|
|
+ color: '#00ff00',
|
|
|
+ name: 'Bob',
|
|
|
+ imageUrlCached: undefined,
|
|
|
+ isActive: false,
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(() => el.dispatchEvent(new Event('click'))).not.toThrow();
|
|
|
+ });
|
|
|
+
|
|
|
+ it('works with clientId 0 (boundary value)', () => {
|
|
|
+ const onClick = vi.fn();
|
|
|
+ const el = createOffScreenIndicator({
|
|
|
+ direction: 'below',
|
|
|
+ clientId: 0,
|
|
|
+ color: '#0000ff',
|
|
|
+ name: 'Zero',
|
|
|
+ imageUrlCached: undefined,
|
|
|
+ isActive: false,
|
|
|
+ onClick,
|
|
|
+ });
|
|
|
+
|
|
|
+ el.dispatchEvent(new Event('click'));
|
|
|
+ expect(onClick).toHaveBeenCalledWith(0);
|
|
|
+ });
|
|
|
+});
|