*/ public static function stored(): array { return ['body_font' => 'Open Sans', 'body_font_size' => 16, 'body_font_height' => 1.8]; } /** * A ThemeOptionsAdapter over $stored: write() sets and deletes like et_update_option/et_delete_option, notes each * call in $writes and returns the read-back; sanitize() runs $sanitize (default: Divi keeps the value). * * @param array $stored * @param list, 1: list}> $writes * @param (Closure(string, mixed): mixed)|null $sanitize */ public static function adapter( array &$stored, array &$writes, ?Closure $sanitize = null, bool $customize = true ): ThemeOptionsAdapter { $options = Mockery::mock(ThemeOptionsAdapter::class); $options->allows('canCustomize')->andReturn($customize); $options->allows('read')->andReturnUsing(static function () use (&$stored): array { return $stored; }); $options->allows('sanitize') ->andReturnUsing($sanitize ?? static fn (string $key, mixed $value): mixed => $value); $options->allows('write')->andReturnUsing( static function (array $set, array $unset) use (&$stored, &$writes): array { $writes[] = [$set, $unset]; $stored = array_diff_key(array_merge($stored, $set), array_flip($unset)); return $stored; } ); return $options; } private function area(array &$stored, array &$writes): ThemeOptionsArea { return new ThemeOptionsArea(self::adapter($stored, $writes)); } public function testAreaReadsThroughTheAdapter(): void { $stored = self::stored(); $writes = []; $area = $this->area($stored, $writes); self::assertSame(['theme_options', 'Theme-Optionen'], [$area->name(), $area->label()]); self::assertSame(DiviFeature::ThemeOptionWrite, $area->feature()); self::assertSame(ThemeOptionBook::fromDivi($stored)->data(), $area->raw()); self::assertSame(ThemeOptionBook::fromDivi($stored)->rev(), $area->rev($stored)); } /** Ruling W6: keys of the planned inventory are set, whitelist keys missing from it are deleted. */ public function testWriteSetsThePlannedKeysAndDeletesTheMissingOnes(): void { $stored = self::stored(); $writes = []; $planned = ['heading_font' => 'Roboto', 'body_font' => 'Open Sans', 'body_font_size' => 18]; $saved = $this->area($stored, $writes)->write($planned, false); self::assertCount(1, $writes); self::assertSame(ThemeOptionBook::fromDivi($planned)->data(), $writes[0][0]); self::assertSame(array_values(array_diff(ThemeOptionSpec::KEYS, array_keys($planned))), $writes[0][1]); self::assertContains('body_font_height', $writes[0][1]); self::assertSame(ThemeOptionBook::fromDivi($planned)->data(), $saved); self::assertArrayNotHasKey('body_font_height', $stored); } /** Ruling W6: a restore (and a rollback) replaces the whole whitelist with the snapshot. */ public function testRestoreReplacesTheWholeWhitelistWithTheSnapshot(): void { $stored = ['body_font_size' => 20, 'tablet_body_font_size' => 18, 'gutter_width' => 2]; $writes = []; $snapshot = ThemeOptionBook::fromDivi(self::stored())->data(); $saved = $this->area($stored, $writes)->restore($snapshot); self::assertSame($snapshot, $saved); self::assertSame($snapshot, ThemeOptionBook::fromDivi($stored)->data()); self::assertContains('tablet_body_font_size', $writes[0][1]); self::assertContains('gutter_width', $writes[0][1]); } /** verify() tolerates only a number stored as another type (15 / "15" / 15.0), nothing else. */ public function testVerifyInsistsOnTheSameKeysAndValues(): void { $stored = self::stored(); $writes = []; $area = $this->area($stored, $writes); $expected = self::stored(); self::assertTrue($area->verify($expected, $expected)); self::assertTrue($area->verify($expected, ['body_font_size' => '16', 'body_font_height' => '1.8'] + $expected)); self::assertFalse($area->verify($expected, ['body_font_size' => 15] + $expected), 'changed value'); self::assertFalse($area->verify($expected, ['body_font' => 'open sans'] + $expected), 'fonts are exact'); $missing = $expected; unset($missing['body_font_height']); self::assertFalse($area->verify($expected, $missing), 'missing key'); self::assertFalse($area->verify($expected, $expected + ['gutter_width' => 3]), 'added key'); self::assertFalse($area->verify(['heading_font' => false], ['heading_font' => '']), 'false is not ""'); } public function testDifferencesNameTheValuesDiviStoredAsAnotherType(): void { $stored = self::stored(); $writes = []; $area = $this->area($stored, $writes); $expected = self::stored(); self::assertSame([], $area->differences($expected, $expected)); self::assertSame( [sprintf( 'Divi hat die Theme-Option ā€ž%sā€œ als ā€ž16ā€œ statt 16 gespeichert.', ThemeOptionSpec::definition('body_font_size')['label'] )], $area->differences($expected, ['body_font_size' => '16'] + $expected) ); } }